【问题标题】:Flutter: show appbar only in some pagesFlutter:仅在某些页面中显示 appbar
【发布时间】:2021-08-24 14:53:16
【问题描述】:

我只想在某些页面有一个appbar,而在其他页面没有appbar,我应该在哪里放置appbar?

目前代码如下所示:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(App());
}

class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: OneContext().builder,
        home: Scaffold(
          body: Home(),
        ),
  }
}

// this widget is needed to change dynamically the body widget maintaining the bottom navbar
class Home extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageWithAppbar(), // dynamically swapped with some PageWithoutAppbar()
      bottomNavigationBar: Nav.instance, // static bottom navbar
    );
  }
}

class PageWithAppbar extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ScaffoldMessenger(
      child: Scaffold(
        appBar: AppBar(), // appbar showing incorrectly
        body: Container(),
      ),
    );
  }
}

PageWithAppbar 中的 appbar 出现在状态栏下方,使用 SafeArea 或 Padding 对其进行填充并不能解决问题,因为填充是固定的(不是设备自适应的),并且顶部会显示为黑色。

如果我在 App 中的 Scaffold 中添加一个 appbar,它会正确显示,但我不想这样做,因为我只想要 PageWithAppbar 中的 appbar 而不是其他页面。 我怎样才能达到让某些页面带有(适当自适应)appbar 而其他页面没有的最终结果?

【问题讨论】:

  • 一个页面/视图/屏幕(不管你怎么称呼它)应该以Scaffold 本身开头。这样做会自动使您能够仅在您想要的位置添加AppBar
  • 您已将 3 个脚手架堆叠在一起。对于不需要 AppBar 的页面,可以省略 Scaffold。
  • @daddygames 正如您正确指出的那样,我删除了 App 中最外层的 Scaffold,但是 Home 中的 appBar 根本没有出现,并且将其添加到 PageWithAppbar 仍然会产生相同的效果。我应该把应用栏放在哪里?
  • Home 中的 Scaffold 需要其 bottomNavigationBar 属性才能应用于所有页面
  • 可能有 100 万种方法来处理这个问题。一个想法:向Home 添加一些内容,以确定AppBar 是否显示在特定视图上。然后将其应用于Home 小部件中的ScaffoldScaffold(appBar: _showAppBar ? MyAppBar() : null, body: MyPage()); 之类的东西您可以使用ChangeNotifier 之类的东西来更新Home 小部件中的_showAppBar

标签: flutter


【解决方案1】:

“硬”的方式

管理 AppBar 的最有效方法是将 Scaffold 放置在每个视图中,而不是像您似乎试图做的那样放在包装器中。然后你可以在每个视图中专门控制应用栏。

因此,使用该解决方案,App 将如下所示:

class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: OneContext().builder,
      home: Home(),
  }
}

...和PageWithScaffold 看起来像这样:

class PageWithAppbar extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(), 
      body: Container(),
    );
  }
}

...和PageWithoutScaffold 看起来像这样:

class PageWithoutAppbar extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
    );
  }
}

使用 CHANGENOTIFIER 的示例应用

但是,如果需要,您可以选择在包装器内使用 Scoffold。我将这个示例应用程序(所有一个文件)放在一起,以展示使用ChangeNotifier 执行此操作的一种可能方法。主App 向通知程序注册了一个侦听器。当某些事情发生变化时,会为App 触发构建,并使其有机会显示或隐藏 AppBar。我试图在相关位置注释代码以解释发生了什么。

这是示例应用程序:

import 'package:flutter/material.dart';

/// this notifier will be used to trigger changes to the global scaffold
/// - used to show/hide the AppBar as desired
/// - used to display the current view (Widget)
/// - used to display the title in the AppBar
class AppNotifier extends ChangeNotifier {

  bool isDisposed = false;
  List<VoidCallback> _listeners = [];

  bool appBar = true;

  Widget currentView;

  String title = "Hello, World!";

  /// call this method to change which Widget is in view
  /// - [title] (default `Hello, World!`) provide a title to show in the AppBar
  /// - [includeAppBar] (default `false`) provide to show/hide the AppBar 
  void show(Widget view, {String title = "Hello, World!", bool includeAppBar = false}) {
    appBar = includeAppBar;
    currentView = view;
    title = title;
    notifyListeners();
  }

  /// call this to show the AppBar
  void showAppBar() {
    appBar = true;
    // the delay is to prevent build errors
    Future.delayed(Duration(milliseconds: 25), () {
      notifyListeners();
    });
  }

  /// call this to hide the AppBar
  void hideAppBar() {
    appBar = false;
    // the delay is to prevent build errors
    Future.delayed(Duration(milliseconds: 25), () {
      notifyListeners();
    });
  }

  @override
  void addListener(listener) {
    _listeners.add(listener);
    super.addListener(listener);
  }

  @override
  void dispose() {
    _listeners.clear();
    isDisposed = true;
    super.dispose();
  }

  @override
  void removeListener(listener) {
    _listeners.remove(listener);
    super.removeListener(listener);
  }

}

// this notifer will be used to update the UI
AppNotifier notifier = new AppNotifier();

Future<void> main() async {
  runApp(App());
}

class App extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AppState();
}

class _AppState extends State<App> {

  // this value is simply used to trigger state changes for the app
  int countChanges = 0;

  @override
  void initState() {
    super.initState();
    notifier.currentView = StartPage();
    notifier.addListener(() {
      // when something changes, trigger a rebuild
      setState(() {
        countChanges = countChanges+1;
      });
    });
  }

  // reset the view back to the StartPage
  void backToStart() {
    notifier.show(StartPage());
  }
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: notifier.appBar // if `true` then we will show the AppBar
          ? AppBar(
            title: Text(notifier.title),
            centerTitle: true,
            leading: notifier.currentView is StartPage ? null : IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                // if on the StartPage, this icon will not appear
                // on other pages, this allows the user to return to the StartPage
                backToStart();
              },
            ),
          )
          : null,
        body: notifier.currentView
      )
    );
  }
  
}

class StartPage extends StatefulWidget {
  StartPage({Key key});
  @override
  State<StatefulWidget> createState() => _StatePageState();
}

class _StatePageState extends State<StartPage> {
  
  @override
  Widget build(BuildContext context) {
    if (!notifier.appBar) {
      // if the appBar is not showing, make it show
      notifier.showAppBar();
    }
    return Container(
      alignment: Alignment.center,
      height: MediaQuery.of(context).size.height,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text("Welcome to Hello World!"),
          Row(children: [
            Expanded(
              flex: 1,
              child:OutlinedButton(
                onPressed: () {
                  // show the PageWithAppBar
                  notifier.show(PageWithAppBar(), title: "Page w/AppBar");
                }, 
                child: Text("Page With Scaffold")
              )
            ),
            Expanded(
              flex: 1,
              child:OutlinedButton(
                onPressed: () {
                  // show the PageWithoutAppBar
                  notifier.show(PageWithoutAppBar());
                }, 
                child: Text("Page Without Scaffold")
              )
            )
          ],)
        ],
      )
        
    );
  }
  
}

class PageWithAppBar extends StatefulWidget {
  PageWithAppBar({Key key});
  @override
  State<StatefulWidget> createState() => _PageWithAppBarState();
}

class _PageWithAppBarState extends State<PageWithAppBar> {
  
  @override
  Widget build(BuildContext context) {
    if (!notifier.appBar) {
      // tell the notifier to update the Scaffold and include the AppBar
      notifier.showAppBar();
    }
    return Container(
      alignment: Alignment.center,
      height: MediaQuery.of(context).size.height,
      child: Text("Hey! This page has an AppBar ?!")
    );
  }

}

class PageWithoutAppBar extends StatefulWidget {
  PageWithoutAppBar({Key key});
  @override
  State<StatefulWidget> createState() => _PageWithoutAppBarState();
}

class _PageWithoutAppBarState extends State<PageWithoutAppBar> {
  
  @override
  Widget build(BuildContext context) {
    if (notifier.appBar) {
      // tell the notifier to update the Scaffold and dispose of the AppBar
      notifier.hideAppBar();
    }
    return Container(
      alignment: Alignment.center,
      height: MediaQuery.of(context).size.height,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text("Boo! This page does not have an AppBar ?."),
          OutlinedButton(
            onPressed: () {
              notifier.show(StartPage());
            }, 
            child: Text("Back to Start")
          )
        ],
      )
        
    );
  }

}

希望这会给您一些关于如何推进您的应用程序的想法。

【讨论】:

    【解决方案2】:

    实际上,下面是上面代码的输出。 AppBar 确实会显示出来,并且用 SafeArea 小部件包裹脚手架确实会在状态栏之后启动 AppBar,而不是从顶部边缘启动。如果这符合您的要求,我建议重新访问您的代码结构或重新安装应用程序。

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          builder: OneContext().builder,
          home: Scaffold(
            body: Home(),
          ),
        );
      }
    }
    
    class Home extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: PageWithAppbar(), // dynamically swapped with some PageWithoutAppbar()
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.done), label: "Ok"),
              BottomNavigationBarItem(icon: Icon(Icons.cancel), label: "Cancel"),
            ],
          ), // static bottom navbar
        );
      }
    }
    
    
    class PageWithAppbar extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return ScaffoldMessenger(
          child: Scaffold(
            appBar: AppBar(
              title: Text("Page with AppBar"),
            ), // appbar showing incorrectly
            body: Center(
              child: Container(
                child: Text("Test"),
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

      【解决方案3】:

      您需要一个变量来帮助您决定创建带有或不带有 appbar 的页面:

      class Home extends StatelessWidget {
      
        late bool showAppBar;
      
        Home({required this.showAppBar}); 
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: showAppBar ? PageWithAppbar() : PageWithoutAppbar(),
            bottomNavigationBar: Nav.instance,
          );
        }
      }
      
      class PageWithAppbar extends StatelessWidget {
      
        @override
        Widget build(BuildContext context) {
          return ScaffoldMessenger(
            child: Scaffold(
              appBar: AppBar(),
              body: Container(),
            ),
          );
        }
      }
      

      您像这样创建Home

      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            builder: OneContext().builder,
            home: Scaffold(
              body: Home(showAppBar: true/false),
            ),
          );
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 2021-08-04
        • 1970-01-01
        • 1970-01-01
        • 2021-05-29
        • 1970-01-01
        相关资源
        最近更新 更多