Flutter:提升AppBar和SliverAppBar

Flutter使用著名的AppBar创建工具栏,但是当我们需要在滑动时显示内容的动态工具栏时,我们使用出色的SliverAppBar小部件。


这两个小部件都可以使应用程序更加美观,毫无疑问,这在Flutter中非常简单。


我在StackOverflow和Facebook小组中看到了很多有关如何更改行为或设计方面的AppBarSliverAppBar的问题。


让我们看两个任务。


任务1


我们想创建一个AppBar ,它不会固定在屏幕顶部,但不像通常那样。 我们要添加一个抽屉 (侧面菜单),在其打开时AppBar将响应。 就是这样:我们自己的AppBar   我们需要的尺寸。


问题是,众所周知, AppBar具有默认大小,我们无法更改它。 看了源代码之后,我们在Scaffold中看到了appBar参数,我们看到它接受了PreferredSizeWidget类型的小部件,现在我们看了AppBar源代码,发现这只是实现PreferredSizeWidget的 StatefulWidget



重点不大:只需创建我们自己的实现PreferredSizeWidget的小部件即可。


那就是我们想要的



当您单击AppBar的菜单按钮时将打开一个侧面菜单。


我们可以通过两种方式做到这一点:


使用`AppBar`


这就是AppBar如何控制Scaffold内侧边栏的打开。


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); } 

使用自定义小部件


在这里,我们具有更大的灵活性,您可以使用ScaffoldState之类的GlobalKeyScaffold中的 InheritedWidget之类的属性 ,从而可以访问用于打开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); } 

结果



简单吧? 让我们看一下SliverAppBar的第二个任务。


任务2


众所周知, SliverAppBar的工作方式如下:



我们想要的是将Card内置到SliverAppBar中 ,如下图所示。



等待,但是SliverAppBar内部的内容被裁剪,因此不能超出该范围?



不用担心,让我们看一下SliverAppBar的源代码,令人惊讶的是,这是StatefulWidget,内部使用了SliverPersistentHeader ,这就是全部秘密。


我们将创建自己的SliverPersistentHeaderDelegate来使用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; } 

结果



完成,两个任务都解决了。


示例在此存储库中


结论


当我们找不到小部件的任何属性时,我们通常会感到绝望,但是我们只需要查看其源代码以了解如何在Flutter中实现它,从而发现如何实现自己的小部件。

Source: https://habr.com/ru/post/zh-CN456054/


All Articles