在所有页面上都存在AppBar抽屉

时间:2018-08-02 18:07:23

标签: dart flutter

我正在尝试创建一个统一的抽屉,该抽屉可在我的应用程序的所有页面上访问。如何使它在所有这些页面中持久存在,而不必在每个dart文件中重新创建自定义抽屉小部件?

5 个答案:

答案 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

Demo project in Github

答案 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),
          ),
        );
    }
  }
}

您可以在这里Navigation Drawer with Multiple Fragments using bloc

找到完整的项目