[英]Sliver Appbar [Collapsing Toolbar] animate title from left to center in Flutter
這是我用於折疊工具欄的構建方法:-
@override
Widget build(BuildContext context) {
return SafeArea(
child: CustomScrollView(
controller: controller,
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: appBarHeight,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Colors.black,
),
onPressed: () => null,
),
floating: true,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.only(left:leftV , bottom:bottomV ),
title: Text(
"Title ",
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
),
),
),
SliverList(delegate:
SliverChildBuilderDelegate((BuildContext context, int index) {
return ListTile(title: Text("Flutter / $index"));
}))
],
),
);
}
根據文檔,我得到了刪除填充的解決方案:-
/// 默認情況下,如果標題未居中,則此屬性的值為 ///
EdgeInsetsDirectional.only(start: 72, bottom: 16)
,否則為EdgeInsetsDirectional.only(start 0, bottom: 16)
。 最終的 EdgeInsetsGeometry titlePadding;
但我得到的 output 是:-
我想在應用欄完全折疊時將標題居中。
問題已在 github 中提交,也請在此處查看。
我自己找到了解決方案!!!
將以下代碼添加到您的Sliver 應用程序欄.....................
flexibleSpace: LayoutBuilder(
builder:
(BuildContext context, BoxConstraints constraints) {
double percent =
((constraints.maxHeight - kToolbarHeight) *
100 /
(appBarHeight - kToolbarHeight));
double dx = 0;
dx = 100 - percent;
if (constraints.maxHeight == 100) {
dx = 0;
}
return Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: kToolbarHeight / 4, left: 0.0),
child: Transform.translate(
child: Text(
title,
style: MyTextStyle.getAppBarTextStyle(
screenUtil, appColors),
),
offset: Offset(
dx, constraints.maxHeight - kToolbarHeight),
),
),
],
);
},
),
百分比是根據滾動計算的,並相應地放置了動畫。
我設法通過ScrollController
獲得了解決方案。
我使用了下一個函數:
double get _horizontalTitlePadding {
const kBasePadding = 15.0;
const kMultiplier = 0.5;
if (_scrollController.hasClients) {
if (_scrollController.offset < (kExpandedHeight / 2)) {
// In case 50%-100% of the expanded height is viewed
return kBasePadding;
}
if (_scrollController.offset > (kExpandedHeight - kToolbarHeight)) {
// In case 0% of the expanded height is viewed
return (kExpandedHeight / 2 - kToolbarHeight) * kMultiplier +
kBasePadding;
}
// In case 0%-50% of the expanded height is viewed
return (_scrollController.offset - (kExpandedHeight / 2)) * kMultiplier +
kBasePadding;
}
return kBasePadding;
}
我在SilverAppBar
titlePadding
使用了它:
child: Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: kExpandedHeight,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: _horizontalTitlePadding),
只需確保在initState()
初始化控制器:
_scrollController = ScrollController()..addListener(() => setState(() {}));
編輯:
我最終創建了一個更好的解決方案,它利用了 FlexibleSpaceBar 中已經發生的轉換。 將文件從 gist 復制到您的項目中后,將FlexibleSpaceBar
替換為MyFlexibleSpaceBar
並提供一個titlePaddingTween
例如
titlePaddingTween: EdgeInsetsTween(begin: EdgeInsets.only(left: 16.0, bottom: 16), end: EdgeInsets.only(left: 72.0, bottom: 16))
而不是 titlePadding。 當應用EdgeInsets
完全展開時,補間將從“開始” EdgeInsets
動畫到當應用EdgeInsets
時“結束” EdgeInsets
。
我還添加了一個顯示在標題和背景上方的foreground
參數,但不會像它們那樣變換。
原答案:
其他答案很好,但它們重建的小部件比必要的要多。 我的解決方案建立在其他答案的基礎上,但只會重建ValueListenableBuilder
:
class SamplePage extends StatelessWidget {
static const _kBasePadding = 16.0;
static const kExpandedHeight = 250.0;
final ValueNotifier<double> _titlePaddingNotifier = ValueNotifier(_kBasePadding);
final _scrollController = ScrollController();
double get _horizontalTitlePadding {
const kCollapsedPadding = 60.0;
if (_scrollController.hasClients) {
return min(_kBasePadding + kCollapsedPadding,
_kBasePadding + (kCollapsedPadding * _scrollController.offset)/(kExpandedHeight - kToolbarHeight));
}
return _kBasePadding;
}
@override
Widget build(BuildContext context) {
_scrollController.addListener(() {
_titlePaddingNotifier.value = _horizontalTitlePadding;
});
return Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: kExpandedHeight,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
centerTitle: false,
titlePadding: EdgeInsets.symmetric(vertical: 16, horizontal: 0),
title: ValueListenableBuilder(
valueListenable: _titlePaddingNotifier,
builder: (context, value, child) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: value),
child: Text(
"Title"),
);
},
),
background: Container(color: Colors.green)
)
),
];
},
body: Text("Body text")
),
);
}
}
我遇到了同樣的問題,我使用LayoutBuilder作為 SliverAppBar 的flexibleSpace小部件的子項解決了它。 LayoutBuilder的目的是讓我知道 appBar 當前的 position(高度)。
我正在使用MediaQuery.of(context).size
來自動獲取屏幕的大小。
var top = 0.0;
var appbarThreshold = 140.0;
class _MySliverAppBarState extends State<MySliverAppBar> {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SliverAppBar(
centerTitle: true,
pinned: true,
leading: TextButton(
child: CircleAvatar(
radius: size.width / 4,
backgroundColor: Colors.blue.withOpacity(0.3),
),
onPressed: () {
print("Hello");
},
),
leadingWidth: size.width / 4,
collapsedHeight: size.height / 11.5,
expandedHeight: size.height / 5,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
top = constraints.biggest.height;
return FlexibleSpaceBar(
title: AnimatedOpacity(
duration: const Duration(milliseconds: 300),
opacity: 1.0,
child: Text(
top < appbarThreshold ? "Bloom" : "Welcome, Iremide",
style: TextStyle(
fontSize: top < appbarThreshold
? size.height / 30
: size.height / 40,
color: Colors.black87,
fontFamily: 'SourceSansSerif',
fontWeight: FontWeight.w700),
),
),
titlePadding: top < appbarThreshold
? EdgeInsets.fromLTRB(
size.width / 4.9, 0.0, 0.0, size.height / 18)
: EdgeInsets.fromLTRB(
size.width / 14, 0.0, 0.0, size.height / 30),
);
},
),
);
}
}
您可以通過在此處編輯左側填充大小來調整 appBar折疊時標題的 Position:
//
titlePadding: top < appbarThreshold
? EdgeInsets.fromLTRB(
size.width / 4.9, 0.0, 0.0, size.height / 18)
: EdgeInsets.fromLTRB(
size.width / 14, 0.0, 0.0, size.height / 30),
問候。
late ScrollController _scrollController;
static const kExpandedHeight = 300.0;
@override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(() => setState(() {}));
}
double get _horizontalTitlePadding {
const kBasePadding = 15.0;
const kMultiplier = 0.5;
if (_scrollController.hasClients) {
if (_scrollController.offset < (kExpandedHeight / 2)) {
// In case 50%-100% of the expanded height is viewed
return kBasePadding;
}
if (_scrollController.offset > (kExpandedHeight - kToolbarHeight)) {
// In case 0% of the expanded height is viewed
return (kExpandedHeight / 2 - kToolbarHeight) * kMultiplier +
kBasePadding;
}
// In case 0%-50% of the expanded height is viewed
return (_scrollController.offset - (kExpandedHeight / 2)) * kMultiplier +
kBasePadding;
}
return kBasePadding;
}
CustomScrollView(
controller: _scrollController,
slivers: [
SliverAppBar(
expandedHeight: kExpandedHeight,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(product.title),
titlePadding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: _horizontalTitlePadding),
background: Hero(
tag: product.id,
child: Image.network(
product.imageUrl,
fit: BoxFit.cover,
),
),
),
),
SliverList(
delegate: SliverChildListDelegate([
//add your widgets here
])
]
) //end of CustomScrollView
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.