SliverAppBar in Flutter
Flutter uses the well-known AppBar to create a toolbar, but when we need a dynamic toolbar that will show content on swipe, we use the excellent SliverAppBar widget.
Both Both widgets allow you to make your application a little prettier, which is no doubt very simple in Flutter.
I have seen many questions on StackOverflow and Facebook groups about how the AppBar and SliverAppBar can be changed in terms of behavior or design.
Let's look at two tasks.
Task 1
We want to create a non-screwed AppBar, but not in the usual way. We want to add a Drawer that the AppBar will respond to when opened. That's it: our own AppBar with the dimensions we need.
The problem is that, as we know, the AppBar has a default size, and we cannot change it. Looking at the source code, we see the AppBar parameter in Scaffold, we see that it accepts a PreferredSizeWidget widget, now we look at the source code of the AppBar and find out that this is only a StatefulWidget that implements PreferredSizeWidget.
This is what we want
How would I do this so that when the menu button of our AppBar is pressed, the side menu opens.
We can do this in two ways:
Using `AppBar`
class Sample1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
drawer: Drawer(),
appBar: MyCustomAppBar(
height: 150,
),
body: Center(
child: FlutterLogo(
size: MediaQuery.of(context).size.width / 2,
),
),
),
);
}
}
class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double height;
const MyCustomAppBar({
Key key,
@required this.height,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.grey[300],
child: Padding(
padding: EdgeInsets.all(30),
child: AppBar(
title: Container(
color: Colors.white,
child: TextField(
decoration: InputDecoration(
hintText: "Search",
contentPadding: EdgeInsets.all(10),
),
),
),
actions: [
IconButton(
icon: Icon(Icons.verified_user),
onPressed: () => null,
),
],
) ,
),
),
],
);
}
@override
Size get preferredSize => Size.fromHeight(height);
}
Using a custom widget
Here we have more flexibility and we can use the GlobalKey of type ScaffoldState or InheritedWidget from Scaffold, thus gaining access to the state methods to open the Drawer.
import 'package:flutter/material.dart';
class Sample1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
drawer: Drawer(),
appBar: MyCustomAppBar(
height: 150,
),
body: Center(
child: FlutterLogo(
size: MediaQuery.of(context).size.width / 2,
),
),
),
);
}
}
class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double height;
const MyCustomAppBar({
Key key,
@required this.height,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.grey[300],
child: Padding(
padding: EdgeInsets.all(30),
child: Container(
color: Colors.red,
padding: EdgeInsets.all(5),
child: Row(children: [
IconButton(
icon: Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
Expanded(
child: Container(
color: Colors.white,
child: TextField(
decoration: InputDecoration(
hintText: "Search",
contentPadding: EdgeInsets.all(10),
),
),
),
),
IconButton(
icon: Icon(Icons.verified_user),
onPressed: () => null,
),
]),
),
),
),
],
);
}
@override
Size get preferredSize => Size.fromHeight(height);
}
Result
Task 2
As we know, SliverAppBar works like this:
Wait, but the content inside the SliverAppBar is being clipped, so it can't go out of bounds, what should I do?
Don't panic, let's take a look at the source code for SliverAppBar and, surprise, this is a StatefulWidget using SliverPersistentHeader internally, that's the secret.
We will create our own SliverPersistentHeaderDelegate to use the SliverPersistentHeader.
class Sample2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
child: CustomScrollView(
slivers: [
SliverPersistentHeader(
delegate: MySliverAppBar(expandedHeight: 200),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => ListTile(
title: Text("Index: $index"),
),
),
)
],
),
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
final double expandedHeight;
MySliverAppBar({@required this.expandedHeight});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
fit: BoxFit.cover,
),
Center(
child: Opacity(
opacity: shrinkOffset / expandedHeight,
child: Text(
"MySliverAppBar",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 23,
),
),
),
),
Positioned(
top: expandedHeight / 2 - shrinkOffset,
left: MediaQuery.of(context).size.width / 4,
child: Opacity(
opacity: (1 - shrinkOffset / expandedHeight),
child: Card(
elevation: 10,
child: SizedBox(
height: expandedHeight,
width: MediaQuery.of(context).size.width / 2,
child: FlutterLogo(),
),
),
),
),
],
);
}
@override
double get maxExtent => expandedHeight;
@override
double get minExtent => kToolbarHeight;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
Result
Done, both tasks solved.
Conclusion:
In the article, I have explained SliverAppBar basic structure in a flutter; Often we despair when we do not find any properties in a widget, but we just need to look at its source code to understand how it is implemented in Flutter, thus discovering options for implementing our own widgets.
❤ ❤ Thanks for reading this article ❤❤
Комментарии
Отправить комментарий