Instagram Profile标题布局在Flutter中
问题内容:
我一直在研究SliverAppBar,CustomScrollView,NestedScrollView,SliverPersistentHeader等。我找不到一种方法来构建类似Instagram用户个人资料屏幕标题的内容,其中仅固定了选项卡栏。屏幕的主体是TabBarView,每个窗格都有一个可滚动的列表。
使用SliverAppBar,可以很容易地在底部参数中添加TabBar。但我想在TabBar上方增加一个未知高度/可变高度的小部件。滚动页面时,多余的窗口小部件应该滚动开,然后TabBar固定在屏幕顶部。
我所能管理的只是标签栏和固定标签栏之前的固定内容。我无法使标题向上滚动并将粘贴TabBar
在的正下方AppBar
。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text("pabloaleko"),
),
body: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverToBoxAdapter(
child: SafeArea(
child: Text("an unknown\namount of content\n goes here in the header"),
),
),
SliverToBoxAdapter(
child: TabBar(
tabs: [
Tab(child: Text('Days', style: TextStyle(color: Colors.black))),
Tab(child: Text('Months', style: TextStyle(color: Colors.black))),
],
),
),
SliverFillRemaining(
child: TabBarView(
children: [
ListView(
children: <Widget>[
ListTile(title: Text('Sunday 1')),
ListTile(title: Text('Monday 2')),
ListTile(title: Text('Tuesday 3')),
ListTile(title: Text('Wednesday 4')),
ListTile(title: Text('Thursday 5')),
ListTile(title: Text('Friday 6')),
ListTile(title: Text('Saturday 7')),
ListTile(title: Text('Sunday 8')),
ListTile(title: Text('Monday 9')),
ListTile(title: Text('Tuesday 10')),
ListTile(title: Text('Wednesday 11')),
ListTile(title: Text('Thursday 12')),
ListTile(title: Text('Friday 13')),
ListTile(title: Text('Saturday 14')),
],
),
ListView(
children: <Widget>[
ListTile(title: Text('January')),
ListTile(title: Text('February')),
ListTile(title: Text('March')),
ListTile(title: Text('April')),
ListTile(title: Text('May')),
ListTile(title: Text('June')),
ListTile(title: Text('July')),
ListTile(title: Text('August')),
ListTile(title: Text('September')),
ListTile(title: Text('October')),
ListTile(title: Text('November')),
ListTile(title: Text('December')),
],
),
],
),
),
],
),
),
);
}
}
问题答案:
您可以使用NestedScrollView
with 实现此行为Scaffold
。
由于我们需要在AppBar
和之间的小部件TabBar
进行动态构建并滚动直到TabBar
到达AppBar
,因此请使用的appBar
属性Scaffold
来构建您的文档,AppBar
并用于headerSliverBuilder
构建其他高度未知的小部件。使用body
of
的属性NestedScrollView
来构建选项卡视图。
这样,的元素headerSliverBuilder
就会滚动直到body
到达底部AppBar
。
仅凭文字可能会使您感到有些困惑,这是给您的一个例子。
码:
// InstaProfilePage
class InstaProfilePage extends StatefulWidget {
@override
_InstaProfilePageState createState() => _InstaProfilePageState();
}
class _InstaProfilePageState extends State<InstaProfilePage> {
double get randHeight => Random().nextInt(100).toDouble();
List<Widget> _randomChildren;
// Children with random heights - You can build your widgets of unknown heights here
// I'm just passing the context in case if any widgets built here needs access to context based data like Theme or MediaQuery
List<Widget> _randomHeightWidgets(BuildContext context) {
_randomChildren ??= List.generate(3, (index) {
final height = randHeight.clamp(
50.0,
MediaQuery.of(context).size.width, // simply using MediaQuery to demonstrate usage of context
);
return Container(
color: Colors.primaries[index],
height: height,
child: Text('Random Height Child ${index + 1}'),
);
});
return _randomChildren;
}
@override
Widget build(BuildContext context) {
return Scaffold(
// Persistent AppBar that never scrolls
appBar: AppBar(
title: Text('AppBar'),
elevation: 0.0,
),
body: DefaultTabController(
length: 2,
child: NestedScrollView(
// allows you to build a list of elements that would be scrolled away till the body reached the top
headerSliverBuilder: (context, _) {
return [
SliverList(
delegate: SliverChildListDelegate(
_randomHeightWidgets(context),
),
),
];
},
// You tab view goes here
body: Column(
children: <Widget>[
TabBar(
tabs: [
Tab(text: 'A'),
Tab(text: 'B'),
],
),
Expanded(
child: TabBarView(
children: [
GridView.count(
padding: EdgeInsets.zero,
crossAxisCount: 3,
children: Colors.primaries.map((color) {
return Container(color: color, height: 150.0);
}).toList(),
),
ListView(
padding: EdgeInsets.zero,
children: Colors.primaries.map((color) {
return Container(color: color, height: 150.0);
}).toList(),
)
],
),
),
],
),
),
),
);
}
}
输出:
希望这可以帮助!