我正在尝试创建一个统一的抽屉,该抽屉可在我的应用程序的所有页面上访问。如何使它在所有这些页面中持久存在,而不必在每个dart文件中重新创建自定义抽屉小部件?
答案 0 :(得分:16)
有几种不同的选择。最基本的希望是您已经完成的事情,但是我还是会列出它:
1:为您的抽屉创建一个类
您的窗口小部件应该是其自己的有状态或无状态窗口小部件。这样,您只需每次都实例化它。
class MyDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(...);
}
}
然后在每个页面中使用它时:
Scaffold(
drawer: MyDrawer(...),
...
)
我希望你已经在这样做了;如果不是,你应该是。类的构建函数不应太大,否则会导致性能降低和难以维护代码;从长远来看,将事物分解成逻辑单元将对您有帮助。
2 :为您的支架创建一个类
如果每页必须在支架中包含相同的抽屉仍然太多代码,则可以改用封装支架的类。本质上,它将为您实际使用的每个支架输入获取输入。
class MyScaffold extends StatelessWidget {
final Widget body;
MyScaffold({this.body});
@override
Widget build(BuildContext context) {
return Scaffold(
body: body,
drawer: MyDrawer(...),
);
}
}
然后使用MyScaffold(而不是在代码中使用Scaffold)(但请使用更好的名称= D)。
3:多层支架
我仅将这种方式包括在内以使其完整,我不建议这样做。话虽这么说,但是在某些情况下,您无法通过flutter的正常工作流程来完成这些工作,例如,当用户在抽屉中的不同项目上轻按时,是否需要自定义动画。
基本上,在这种情况下,您要做的是在MaterialApp或Navigator之外安装一个Scaffold(我相信这也意味着您必须在此之外再安装另一个Navigator,但我不确定100%确定)。您将在导航之外的支架上显示抽屉,而另一个支架(在导航的每个页面上)将做您需要做的其他事情。有一些警告-您必须确保获得正确的脚手架(即Scaffold.of(context)
本身不会削减它-您必须获取第一个脚手架的上下文并使用它来查找较高的级别),您可能需要将(较低级别的脚手架的)GlobalKey传递给Drawer,以便它实际上可以更改其中的页面。
正如我所说,我不建议您采用这种方法,因此,我不会再详细介绍它,而是让读者在想钻进兔子窝的时候作为练习来做! / p>
答案 1 :(得分:8)
rmtmckenzie非常正确。
尽管您对多功能支架解决方案感到好奇,但它可能比您想象的要优雅。
要在所有页面之间共享抽屉,我们可以在builder
实例中添加MaterialApp
。
这将实例化Scaffold
下但在所有路径上方的Navigator
。
MaterialApp(
title: 'Flutter Demo',
builder: (context, child) {
return Scaffold(
drawer: MyDrawer(),
body: child,
);
},
home: MyHome()
)
在页面内部,您可以像平常一样不受限制地实例化另一个Scaffold
。
然后您可以通过在 MaterialApp
下的任何小部件中执行以下操作来显示共享抽屉:
final ScaffoldState scaffoldState = context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();
您可以提取为好帮手的代码:
class RootScaffold {
static openDrawer(BuildContext context) {
final ScaffoldState scaffoldState =
context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();
}
}
然后使用RootScaffold.openDrawer(context)
答案 2 :(得分:3)
除了@Rémi Rousselet答案
SELECT * FROM tbl_abc t WHERE t.id = '1' IN (SELECT mr.param_filter
FROM tbl_my_report mr WHERE mr.id = '101' );
对于根抽屉中的导航,如果您使用MaterialApp(
title: 'Flutter Demo',
builder: (context, child) {
return Scaffold(
drawer: MyDrawer(),
body: child,
);
},
home: MyHome()
)
会引发错误,并且必须使用子窗口小部件来导航到其他页面
喜欢
Navigator.of(context) // push or pop
答案 3 :(得分:0)
如果有人在导航时正在寻找奇特的东西,请看这里。我用作项目抽屉的是flutter_inner_drawer包。
我创建了一个名为CustomDrawer的有状态类。
class CustomDrawer extends StatefulWidget {
final Widget scaffold;
final GlobalKey<InnerDrawerState> innerDrawerKey;
CustomDrawer({
Key key,
this.scaffold,
this.innerDrawerKey,
}) : super(key: key);
@override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
MainPageIcons assets = MainPageIcons();//From my actual code dont care it
final vars = GlobalVars.shared; //From my actual code dont care it
@override
Widget build(BuildContext context) {
return InnerDrawer(
key: widget.innerDrawerKey,
onTapClose: true, // default false
tapScaffoldEnabled: true,
swipe: true, // default true
colorTransition: Colors.teal, // default Color.black54
//innerDrawerCallback: (a) => print(a ),// return bool
leftOffset: 0.2, // default 0.4
leftScale: 1,// default 1
boxShadow: [
BoxShadow(color: Colors.teal,blurRadius: 20.0, // has the effect of softening the shadow
spreadRadius: 10.0, // has the effect of extending the shadow
offset: Offset(
10.0, // horizontal, move right 10
10.0, // vertical, move down 10
),)],
borderRadius: 20, // default 0
leftAnimationType: InnerDrawerAnimation.quadratic, // default static
//when a pointer that is in contact with the screen and moves to the right or left
onDragUpdate: (double val, InnerDrawerDirection direction) =>
setState(() => _dragUpdate = val),
//innerDrawerCallback: (a) => print(a),
// innerDrawerCallback: (a) => print(a), // return true (open) or false (close)
leftChild: menus(), // required if rightChild is not set
scaffold:widget.scaffold
);
}
double _dragUpdate = 0;
Widget menus(){
return
Material(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
ColorTween(
begin: Colors.blueAccent,
end: Colors.blueGrey[400].withRed(100),
).lerp(_dragUpdate),
ColorTween(
begin: Colors.green,
end: Colors.blueGrey[800].withGreen(80),
).lerp(_dragUpdate),
],
),
),
child: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10, bottom: 15),
width: 80,
child: ClipRRect(
child: Image.network(
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrWfWLnxIT5TnuE-JViLzLuro9IID2d7QEc2sRPTRoGWpgJV75",
),
borderRadius: BorderRadius.circular(60),
),
),
Text(
"User",
style: TextStyle(color: Colors.white, fontSize: 18),
)
],
//mainAxisAlignment: MainAxisAlignment.center,
),
Padding(
padding: EdgeInsets.all(10),
),
ListTile(
onTap: ()=>navigate(Profile.tag),
title: Text(
"Profile",
style: TextStyle(color: Colors.white, fontSize: 14),
),
leading: Icon(
Icons.dashboard,
color: Colors.white,
size: 22,
),
),
ListTile(
title: Text(
"Camera",
style: TextStyle(fontSize: 14,color:Colors.white),
),
leading: Icon(
Icons.camera,
size: 22,
color: Colors.white,
),
onTap: ()=>navigate(Camera.tag)
),
ListTile(
title: Text(
"Pharmacies",
style: TextStyle(fontSize: 14,color:Colors.white),
),
leading: Icon(
Icons.add_to_photos,
size: 22,
color: Colors.white,
),
onTap: ()=>navigate(Pharmacies.tag)
),
],
),
),
Positioned(
bottom: 20,
child: Container(
alignment: Alignment.bottomLeft,
margin: EdgeInsets.only(top: 50),
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 25),
width: double.maxFinite,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
Icons.all_out,
size: 18,
color: Colors.grey,
),
Text(
" LogOut",
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
),
)
],
),
),
_dragUpdate < 1
? BackdropFilter(
filter: ImageFilter.blur(
sigmaX: (10 - _dragUpdate * 10),
sigmaY: (10 - _dragUpdate * 10)),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0),
),
),
)
: null,
].where((a) => a != null).toList(),
));
}
navigate(String route) async{
await navigatorKey.currentState.pushNamed(route).then((_){
Timer(Duration(milliseconds: 500),()=>widget.innerDrawerKey.currentState.toggle() );
});
}
}
我从包装中复制了示例,但接触得并不多。只是增加了回退后切换的功能。
navigate(String route) async{
await navigatorKey.currentState.pushNamed(route).then((_){
Timer(Duration(milliseconds: 500),()=>widget.innerDrawerKey.currentState.toggle() );
});
}
从全局导航到具有GlobalKey的所有页面,以便每个类均可访问
final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: "Main Navigator");
inner_drawer还需要一个用于状态切换的全局键,但是如果在页面之间导航时仅创建一个,则会出现重复的全局键错误。为了避免我创建了一个名为innerKeys的全局变量
Map<String,GlobalKey<InnerDrawerState>>innerKeys={
'main':GlobalKey<InnerDrawerState>(),
'profile':GlobalKey<InnerDrawerState>(),
'pharmacies':GlobalKey<InnerDrawerState>(),
};
最后我将此CustomDrawer添加到每个页面
@override
Widget build(BuildContext context) {
return CustomDrawer(
innerDrawerKey: vars.innerKeys['profile'],
scaffold:Scaffold(
appBar: CustomAppBar(
title: 'Profile',
actions: <Widget>[
],),
body: Stack(
children: <Widget>[
Background(),
])));
}
我希望它会对某人有所帮助。
注意:请检查原始的绒毛包装是否有任何更新。请注意,此示例并不完美,需要注意,如果在此抽屉上进行许多导航,则小部件树将具有许多页面,并且性能会受到影响。任何调优建议都会得到应用。
答案 4 :(得分:0)
使用bloc程序包的具有多个片段的我的解决方案导航抽屉
首先,在dependencies
文件的pubspec.yaml
下方添加
flutter_bloc: ^4.0.0
现在创建以下文件
drawer_event.dart
import 'nav_drawer_state.dart';
abstract class NavDrawerEvent {
const NavDrawerEvent();
}
class NavigateTo extends NavDrawerEvent {
final NavItem destination;
const NavigateTo(this.destination);
}
nav_drawer_bloc.dart
import 'package:bloc/bloc.dart';
import 'drawer_event.dart';
import 'nav_drawer_state.dart';
class NavDrawerBloc extends Bloc<NavDrawerEvent, NavDrawerState> {
@override
NavDrawerState get initialState => NavDrawerState(NavItem.homePage);
@override
Stream<NavDrawerState> mapEventToState(NavDrawerEvent event) async* {
if (event is NavigateTo) {
if (event.destination != state.selectedItem) {
yield NavDrawerState(event.destination);
}
}
}
}
nav_drawer_state.dart
class NavDrawerState {
final NavItem selectedItem;
const NavDrawerState(this.selectedItem);
}
enum NavItem {
homePage,
profilePage,
orderPage,
myCart,
}
drawer_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/drawer_event.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_state.dart';
class NavDrawerWidget extends StatelessWidget {
final String accountName;
final String accountEmail;
final List<_NavigationItem> _listItems = [
_NavigationItem(true, null, null, null),
_NavigationItem(false, NavItem.homePage, "Home", Icons.home),
_NavigationItem(false, NavItem.profilePage, "Profile Page", Icons.person),
_NavigationItem(false, NavItem.orderPage, "My Orders", Icons.list),
_NavigationItem(false, NavItem.myCart, "My Cart", Icons.shopping_cart),
];
NavDrawerWidget(this.accountName, this.accountEmail);
@override
Widget build(BuildContext context) => Drawer(
child: Container(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: _listItems.length,
itemBuilder: (BuildContext context, int index) =>
BlocBuilder<NavDrawerBloc, NavDrawerState>(
builder: (BuildContext context, NavDrawerState state) =>
_buildItem(_listItems[index], state),
)),
));
Widget _buildItem(_NavigationItem data, NavDrawerState state) =>
data.header ? _makeHeaderItem() : _makeListItem(data, state);
Widget _makeHeaderItem() => UserAccountsDrawerHeader(
accountName: Text(accountName, style: TextStyle(color: Colors.white)),
accountEmail: Text(accountEmail, style: TextStyle(color: Colors.white)),
decoration: BoxDecoration(color: Colors.indigo),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
foregroundColor: Colors.amber,
child: Icon(
Icons.person,
size: 54,
),
),
);
Widget _makeListItem(_NavigationItem data, NavDrawerState state) => Card(
shape: ContinuousRectangleBorder(borderRadius: BorderRadius.zero),
borderOnForeground: true,
elevation: 0,
margin: EdgeInsets.zero,
child: Builder(
builder: (BuildContext context) => ListTile(
title: Text(
data.title,
style: TextStyle(
color: data.item == state.selectedItem ? Colors.green : Colors.blueGrey,
),
),
leading: Icon(
data.icon,
color: data.item == state.selectedItem ? Colors.green : Colors.blueGrey,
),
onTap: () => _handleItemClick(context, data.item),
),
),
);
void _handleItemClick(BuildContext context, NavItem item) {
BlocProvider.of<NavDrawerBloc>(context).add(NavigateTo(item));
Navigator.pop(context);
}
}
class _NavigationItem {
final bool header;
final NavItem item;
final String title;
final IconData icon;
_NavigationItem(this.header, this.item, this.title, this.icon);
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_state.dart';
import 'package:flutterdrawerwithbloc/drawer_widget.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Navigation Drawer Demo',
theme: ThemeData(primarySwatch: Colors.blue, scaffoldBackgroundColor: Colors.white),
home: MyHomePage(),
);
;
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
NavDrawerBloc _bloc;
Widget _content;
@override
void initState() {
super.initState();
_bloc = NavDrawerBloc();
_content = _getContentForState(_bloc.state.selectedItem);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) => BlocProvider<NavDrawerBloc>(
create: (BuildContext context) => _bloc,
child: BlocListener<NavDrawerBloc, NavDrawerState>(
listener: (BuildContext context, NavDrawerState state) {
setState(() {
_content = _getContentForState(state.selectedItem);
});
},
child: BlocBuilder<NavDrawerBloc, NavDrawerState>(
builder: (BuildContext context, NavDrawerState state) => Scaffold(
drawer: NavDrawerWidget("AskNilesh", "rathodnilsrk@gmail.com"),
appBar: AppBar(
title: Text(_getAppbarTitle(state.selectedItem)),
centerTitle: false,
brightness: Brightness.light,
backgroundColor: Colors.indigo,
),
body: AnimatedSwitcher(
switchInCurve: Curves.easeInExpo,
switchOutCurve: Curves.easeOutExpo,
duration: Duration(milliseconds: 300),
child: _content,
),
),
),
));
_getAppbarTitle(NavItem state) {
switch (state) {
case NavItem.homePage:
return 'Home';
case NavItem.profilePage:
return 'Profile Page';
case NavItem.orderPage:
return 'My Orders';
case NavItem.myCart:
return 'My Cart';
default:
return '';
}
}
_getContentForState(NavItem state) {
switch (state) {
case NavItem.homePage:
return Center(
child: Text(
'Home Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.profilePage:
return Center(
child: Text(
'Profile Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.orderPage:
return Center(
child: Text(
'My Orders',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.myCart:
return Center(
child: Text(
'My Cart',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
default:
return Center(
child: Text(
'Home Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
}
}
}
找到完整的项目