How to make FlexibleSpaceBar in SliverAppBar honour SafeArea?.
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
title: FittedBox(
fit: BoxFit.fitWidth,
child: Image.asset('assets/images/user.png')),
),
),
SliverList(
delegate: SliverChildListDelegate([
TextField(),
]),
)
],
)
I need the image be below os header at all time
I tried to wrap it with SafeArea widget but that didn't work and crashed
The following should work:
class TestSafeArea extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
delegate: SafeAreaPersistentHeaderDelegate(
expandedHeight: 200,
child: Image.asset('assets/YOUR_IMAGE.png'))),
SliverList(
delegate: SliverChildListDelegate([
TextField(),
]),
)
],
),
);
}
}
class SafeAreaPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
final double expandedHeight;
SafeAreaPersistentHeaderDelegate({this.child, this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SafeArea(bottom: false, child: SizedBox.expand(child: child));
}
#override
double get maxExtent => expandedHeight;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SafeAreaPersistentHeaderDelegate old) {
if (old.child != child) {
return true;
}
return false;
}
}
Sorry about the confusion!
EDIT #2 - Just saw you don't want the entire AppBar in the SafeArea
class SafeAreaPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget title;
final Widget flexibleSpace;
final double expandedHeight;
SafeAreaPersistentHeaderDelegate(
{this.title, this.flexibleSpace, this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final Widget appBar = FlexibleSpaceBar.createSettings(
minExtent: minExtent,
maxExtent: maxExtent,
currentExtent: max(minExtent, maxExtent - shrinkOffset),
toolbarOpacity: 1,
child: AppBar(
backgroundColor: Colors.blue,
automaticallyImplyLeading: false,
title: title,
flexibleSpace: (title == null && flexibleSpace != null)
? Semantics(child: flexibleSpace, header: true)
: flexibleSpace,
toolbarOpacity: 1,
bottomOpacity: 1.0),
);
return appBar;
}
#override
double get maxExtent => expandedHeight;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SafeAreaPersistentHeaderDelegate old) {
if (old.flexibleSpace != flexibleSpace) {
return true;
}
return false;
}
}
This will give your desired effect.
By using a SliverPersistentHeader with a custom SliverPersistentHeaderDelegate that returns an AppBar wrapped in the SafeArea widget.
class TestSafeArea extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
delegate: SafeAreaPersistentHeaderDelegate(
expandedHeight: 200,
flexibleSpace: SafeArea(
child: Container(
color: Colors.red,
),
)),
),
SliverList(
delegate: SliverChildListDelegate([
TextField(),
]),
)
],
),
);
}
}
Related
How to make this sliver flutter?
I want to do the same thing as in the following video
enter image description here
You can do like this.
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
title: Text("App bar"),
),
SliverPersistentHeader(
delegate: DelegateHeader(),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index){
return ListTile(
title: Text("new item $index"),
);
},
childCount: 50
)
)
],
),
);
}
and
class DelegateHeader extends SliverPersistentHeaderDelegate {
#override
double get maxExtent => 100;
#override
double get minExtent => 100;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.black26,
child: Center(child: Text("Always here")),
);
}
}
I'm trying to display tabs for each main tab (Nested Tab Bar) in SliverAppBar(). It's look like this:
See the image
See the GIF
The content of the exam tab it's in Container() widget (That the error in the image came from).
Now, with the Container() widget the SliverAppBar() will collapse when the user scroll the exam tab content (white screen in the image), everything is fine for now.
So, After I replaced the Container() with ListView.builder() to make the tab content scrollable, now I can't collapse SliverAppBar() from the tab content (white screen in the image). but I can from the SliverAppBar().
See this GIF after I added ListView.builder()
So, How I can make the SliverAppBar scrollable (collapsing ) with Listview?
Can anyone help me? please :(
This example (demo):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'SliverAppBar App Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return [
SliverOverlapAbsorber(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverSafeArea(
top: false,
sliver: SliverAppBar(
pinned: true,
title: Text(widget.title),
expandedHeight: 500,
),
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(tabs: [Tab(text: 'Tab A'), Tab(text: 'Tab B')]),
Colors.blue,
),
pinned: false,
),
];
},
body: TabBarView(
children: <Widget>[
NestedTabs('A'),
NestedTabs('B'),
],
),
),
),
),
);
}
}
// This class is to handle the main tabs (Tab A & Tab B)
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar, this._color);
TabBar _tabBar;
final Color _color;
#override
double get minExtent => _tabBar.preferredSize.height;
#override
double get maxExtent => _tabBar.preferredSize.height;
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
color: _color,
alignment: Alignment.center,
child: _tabBar,
);
}
#override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
class NestedTabs extends StatelessWidget {
final String mainTabName;
NestedTabs(this.mainTabName);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(20),
child: Container(
color: Colors.blue,
alignment: Alignment.bottomCenter,
child: TabBar(
tabs: [
Tab(text: 'Tab $mainTabName-1'),
Tab(text: 'Tab $mainTabName-2')
],
),
),
),
body: TabBarView(
children: [
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: 500,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
width: 200,
color: Colors.black45,
child: Center(child: Text('Index ${index}')));
},
),
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: 500,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
width: 200,
color: Colors.black45,
child: Center(child: Text('Index ${index}')));
},
)
],
),
);
}
}
Thank you :)
Use SliverList() instead of SliverFillRemaining for ListView
How I can get such effect of AppBar title, sample:
The big title "Coffee shop" disappears when page scrolls and appears in appBar. Thanks.
probably you need SliverAppBar
You can also check this tutorial
An example based on the article:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainCollapsingToolbar());
}
}
class MainCollapsingToolbar extends StatefulWidget {
#override
_MainCollapsingToolbarState createState() => _MainCollapsingToolbarState();
}
class _MainCollapsingToolbarState extends State<MainCollapsingToolbar> {
ScrollController _scrollController = ScrollController();
double offset = 0.0;
#override
void initState() {
super.initState();
_scrollController.addListener(() {
setState(() {
offset = _scrollController.offset;
});
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: (offset == 0) ? false : true,
title: Text("Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(
labelColor: Colors.black87,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(icon: Icon(Icons.info), text: "Tab 1"),
Tab(icon: Icon(Icons.lightbulb_outline), text: "Tab 2"),
],
),
),
pinned: true,
),
];
},
body: Center(
child: Text("Sample text"),
),
),
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
#override
double get minExtent => _tabBar.preferredSize.height;
#override
double get maxExtent => _tabBar.preferredSize.height;
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
child: _tabBar,
);
}
#override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
I'm following this tutorial, but using flutter web
I came across this issue with my code:
#override
Widget build(BuildContext context) {
final Size _size = MediaQuery.of(context).size;
return Center(
child: Container(
height: _size.height,
width: maxWidth(_size),
color: indigo,
child: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
floating: true,
delegate: SliverHeaderDelegate(
minExtent: _size.height*0.1,
maxExtent: _size.height*0.5),),
SliverFillRemaining()
],),),);}}
where the header delegate is:
class SliverHeaderDelegate implements SliverPersistentHeaderDelegate {
SliverHeaderDelegate({
this.minExtent,
#required this.maxExtent,
});
final double minExtent;
final double maxExtent;
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent)=>
Center(
child: Container(
child:Text('test',textScaleFactor: 5,),
);
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate)=> true;
#override
FloatingHeaderSnapConfiguration get snapConfiguration => null;
}
gives this result
but if I replace the SliverPersistentHeader with this:
SliverAppBar( pinned: true,
expandedHeight: _size.height*0.5,
flexibleSpace: FlexibleSpaceBar(
title: Text('test',textScaleFactor: 5,),
),),
the result changes drastically as you can see here
any idea why and how to correct it?
thanks
I've seen new flutter video and seen some interesting. (It's not typical sticky header or expandable list, so I don't know how to name it)
Video - watch from 0:20
Does anybody know how can I create such type of list with headers using SliverList?
One way is to create a CustomScrollView and pass a SliverAppBar pinned to true and a SliverFixedExtentList object with your Widgets.
Example:
List<Widget> _sliverList(int size, int sliverChildCount) {
var widgetList = <Widget>[];
for (int index = 0; index < size; index++)
widgetList
..add(SliverAppBar(
title: Text("Title $index"),
pinned: true,
))
..add(SliverFixedExtentList(
itemExtent: 50.0,
delegate:
SliverChildBuilderDelegate((BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('list item $index'),
);
}, childCount: sliverChildCount),
));
return widgetList;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Slivers"),
),
body: CustomScrollView(
slivers: _sliverList(50, 10),
),
);
}
SliverPersistentHeader is the more generic widget behind SliverAppBar that you can use.
SliverPersistentHeader(
delegate: SectionHeaderDelegate("Section B"),
pinned: true,
),
And the SectionHeaderDelegate can be implement with something like:
class SectionHeaderDelegate extends SliverPersistentHeaderDelegate {
final String title;
final double height;
SectionHeaderDelegate(this.title, [this.height = 50]);
#override
Widget build(context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Theme.of(context).primaryColor,
alignment: Alignment.center,
child: Text(title),
);
}
#override
double get maxExtent => height;
#override
double get minExtent => height;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => false;
}