草庐IT

Flutter - 从页面修改 AppBar

coder 2023-07-22 原文

所以我有一个包含多个页面的 Flutter 应用程序,这是通过 PageView 完成的。在此页面 View 之前,我创建了我的 AppBar,因此它始终位于应用程序的顶部,并且在页面之间滚动时不会显示动画。 然后我想在其中一个页面上创建一个底部应用栏,但为此我需要访问应用栏元素,但我不知道如何执行此操作。

这是主类,我试图在其上编辑应用栏的页面是 PlanPage

final GoogleSignIn googleSignIn = GoogleSignIn();
final FirebaseAuth auth = FirebaseAuth.instance;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: '',
            home: _handleCurrentScreen()
        );
    }

    Widget _handleCurrentScreen() {
        return StreamBuilder<FirebaseUser>(
            stream: auth.onAuthStateChanged,
            builder: (BuildContext context, snapshot) {
                print(snapshot);
                if (snapshot.connectionState == ConnectionState.waiting) {
                    return SplashPage();
                } else {
                    if (snapshot.hasData) {
                        return Home();
                    }
                    return LoginPage();
                }
            }
        );
    }
}

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
      return HomeState();
  }
}

class HomeState extends State<Home> {
    PageController _pageController;

    PreferredSizeWidget bottomBar;

    int _page = 0;


  @override
  Widget build(BuildContext context) {
      return Scaffold(
          appBar: AppBar(
              bottom: bottomBar,
          ),
          body: PageView(
              children: [
                  Container(
                      child: SafeArea(
                          child: RecipesPage()
                      ),
                  ),
                  Container(
                      child: SafeArea(
                          child: PlanPage()
                      ),
                  ),
                  Container(
                      child: SafeArea(
                          child: ShoppingListPage()
                      ),
                  ),
                  Container(
                      child: SafeArea(
                          child: ExplorePage()
                      ),
                  ),
              ],

              /// Specify the page controller
              controller: _pageController,
              onPageChanged: onPageChanged
          ),
          bottomNavigationBar: BottomNavigationBar(
              type: BottomNavigationBarType.fixed,
              items: [
                  BottomNavigationBarItem(
                      icon: Icon(Icons.book),
                      title: Text('Recipes')
                  ),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.event),
                      title: Text('Plan')
                  ),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.shopping_cart),
                      title: Text('Shopping List')
                  ),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.public),
                      title: Text("Explore"),
                  ),
              ],
              onTap: navigationTapped,
              currentIndex: _page,
          ),

      );
  }

    void onPageChanged(int page){
        setState((){
            this._page = page;
        });
    }

    void setBottomAppBar(PreferredSizeWidget appBar) {
        this.bottomBar = appBar;
        print("setBottomAppBar: "+ appBar.toString());
    }

    /// Called when the user presses on of the
    /// [BottomNavigationBarItem] with corresponding
    /// page index
    void navigationTapped(int page){

        // Animating to the page.
        // You can use whatever duration and curve you like
        _pageController.animateToPage(
            page,
            duration: const Duration(milliseconds: 300),
            curve: Curves.ease
        );
    }

    @override
    void initState() {
        super.initState();
        initializeDateFormatting();

        _pageController = PageController();
    }

    @override
    void dispose(){
        super.dispose();
        _pageController.dispose();
    }
}

PlanPage 类如下所示

class PlanPage extends StatefulWidget {
    var homeState;

    PlanPage(this.homeState);

    @override
    State<StatefulWidget> createState() {
        return _PlanState(homeState);
    }

}

class _PlanState extends State<PlanPage> with AutomaticKeepAliveClientMixin<PlanPage>, SingleTickerProviderStateMixin {
    var homeState;
    TabController _tabController;

    _PlanState(this.homeState);

    @override
    bool get wantKeepAlive => true;

    @override
    Widget build(BuildContext context) {
        //homeState.setBottomAppBar(_buildTabBar());

        return Scaffold(
            appBar: AppBar(
                bottom: _buildTabBar(),
            ),
            body: TabBarView(
                controller: _tabController,
                children: Plan.now().days.map((day) {
                    return ListView.builder(
                        itemCount: MealType.values.length,
                        itemBuilder: (BuildContext context, int index){
                            var mealType = MealType.values[index];
                            return Column(
                                children: <Widget>[
                                    Text(
                                        mealType.toString().substring(mealType.toString().indexOf('.')+1),
                                        style: TextStyle(
                                            //decoration: TextDecoration.underline,
                                            fontSize: 30.0,
                                            fontWeight: FontWeight.bold
                                        ),
                                    ),
                                    Column(
                                        children: day.meals.where((meal) => meal.mealType == mealType).map((meal) {
                                            return RecipeCard(meal.recipe);
                                        }).toList(),
                                    )

                                ],
                            );
                        }
                    );
                }).toList(),
            )
        );
    }

    Widget _buildTabBar() {
        return TabBar(
            controller: _tabController,
            isScrollable: true,
            tabs: List.generate(Plan.now().days.length,(index) {
                return Tab(
                    child: Column(
                        children: <Widget>[
                            Text(DateFormat.E().format(Plan.now().days[index].day)),
                            Text(DateFormat('d/M').format(Plan.now().days[index].day)),
                        ],
                    ),
                );
            }, growable: true),
        );
    }

    @override
    void initState() {
        super.initState();

        _tabController = new TabController(
            length: Plan.now().days.length,
            vsync: this,
            initialIndex: 1
        );
    }
}

不管它现在的工作方式如何,它都会显示 2 个应用程序栏。[

最佳答案

通常,拥有两个嵌套的可滚​​动区域并不是最佳做法。两个嵌套的脚手架也是如此。

也就是说,您可以监听页面更改 (_pageController.addListener(listener) ) 以更新 page 状态属性,并构建不同的 AppBar。 bottom(在 Home 小部件中,因此您可以删除 PlanPage 中的 Scaffold),具体取决于用户正在查看的页面。

-编辑-

在您的 Home 小部件中,您可以像这样向 _pageController 添加一个监听器:

void initState() {
    super.initState();
    _pageController = PageController()
        ..addListener(() {
            setState(() {});
        });
}

每次用户在您的 PageView 中滚动时都重建您的小部件。使用空函数调用 setState 可能看起来令人困惑,但它只是允许您在 _pageController.page 更改时重建小部件,这不是默认行为。您还可以拥有一个 page 状态属性并在 setState 调用中更新它以反射(reflect) _pageController.page 属性,但结果将是一样。

通过这种方式,您可以根据 _pageController.page 构建不同的 AppBar.bottom:

// in your build function

final bottomAppBar = _pageController.page == 2 ? TabBar(...) : null;

final appBar = AppBar(
    bottom: bottomAppBar,
    ...
);

return Scaffold(
    appBar: appBar,
    ...
);

关于Flutter - 从页面修改 AppBar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53461043/

有关Flutter - 从页面修改 AppBar的更多相关文章

  1. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  2. ruby - 在 ASP 页面上 Mechanize 中断 - 2

    require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie

  3. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  4. ruby - 每个页面上的 Jekyll 分页 - 2

    据我们所知,Jekyll默认分页仅支持index.html,我想创建blog.html并在那里包含分页。有什么解决办法吗? 最佳答案 如果您创建一个名为/blog的目录并在其中放置一个index.html文件,那么您可以向_config.yml表示paginate_path:"blog/page:num"。不是使用根文件夹中的默认index.html作为分页器模板,而是使用/blog/index.html。分页器将根据需要生成类似/blog/page2/和/blog/page3/的页面。这将使您到达yourwebsite.com/b

  5. ruby-on-rails - RoR && "coming soon"页面 - 2

    我正在寻找一种简单的方法来为我在RubyonRails上的项目实现简单的“即将推出”(预启动)页面。用户应该能够留下电子邮件以便在项目启动时收到通知。有没有这样的插件\gem?或者我应该自己做... 最佳答案 LaunchingSoon是一个Rails插件。它还集成了MailChimp或Campaignmonitor. 关于ruby-on-rails-RoR&&"comingsoon"页面,我们在StackOverflow上找到一个类似的问题: https:/

  6. ruby - 如何让 GitHub 页面使用 master 分支? - 2

    我有一个使用Jekyll托管在GitHub上的静态网站。问题是,我真的不需要master分支,因为存储库唯一包含的是网站。这样我就必须gitcheckoutgh-pages,然后gitmergemaster,然后gitpushorigingh-pages。有什么简单的方法可以摆脱gh-pages分支并直接从master推送? 最佳答案 Theproblemis,Idon'treallyneedthemasterbranch,astheonlythingtherepositorycontainsisthewebsite.Isthere

  7. ruby - 如何设置 Mechanize 页面编码? - 2

    我试图通过点击一个链接获得一个带有ISO-8859-1编码的页面,所以代码类似于这样:page_result=page.link_with(:text=>'link_text').click到目前为止,我得到的结果编码错误,所以我看到的字符如下:'T�tulo:'insteadof'Título:'我尝试了几种方法,包括:使用代理在第一个请求中声明编码:@page_search=@agent.get(:url=>'http://www.server.com',:headers=>{'Accept-Charset'=>'ISO-8859-1'})说明页面本身的编码page_result.

  8. ruby-on-rails - Ruby on Rails - 我可以在调用之前修改属性的值吗? - 2

    假设我有一个名为Product的模型,其中有一个名为brand的字段。假设brand的值以this_is_a_brand格式存储。我可以在模型(或其他任何地方)中定义一个方法,允许我在调用brand之前修改它的值吗?例如,如果我调用@product.brand,我想得到ThisisaBrand,而不是this_is_a_brand。 最佳答案 我建议使用方括号语法([]和[]=)而不是read_attribute和write_attribute。方括号语法更短并且designedtowraptheprotectedread/writ

  9. ruby-on-rails - 仅在某些页面上使用 rails_xss - 2

    我正在使用rails_xss运行Rails2.3.14插入。我有另一个用于创建管理仪表板View的插件。我的问题是rails_xss正在转义我的仪表板插件生成的所有HTML。有没有一种方法可以将rails_xss配置为不转义匹配example.com/admin或基于目录(app/views/admin)或任何类似的页面结果一样吗? 最佳答案 更新仪表板生成插件以使用raw或html_safe进行内容输出可能会更简单。 关于ruby-on-rails-仅在某些页面上使用rails_xss

  10. ruby - 无论如何要在 REPL 中重新加载修改后的 gem 文件 - 2

    在尝试构建Rubygem(使用Bundler)时,我倾向于使用Bundler提供的REPL测试代码——可通过bundleconsole访问。有什么方法可以重新加载整个项目吗?我最终再次加载单个(更改的)文件以测试新更改。 最佳答案 以下hack适用于我的一个相对简单的gem和Ruby2.2.2。我很想看看它是否适合你。它做出以下假设:您具有传统的文件夹结构:一个名为lib/my_gem_name.rb的文件和一个文件夹lib/my_gem_name/,其中包含任何文件/文件夹结构。您要重新加载的所有类都嵌套在您的顶级模块MyGemN

随机推荐