zoukankan      html  css  js  c++  java
  • Flutter学习之制作底部菜单导航

    简介

    现在我们的 APP 上面都会在屏幕下方有一排的按钮,点击不同的按钮可以进入不同的界面。就是说在界面的底部会有一排的按钮导航。可看下面的图示。

    完成图示 

    程序工程目录 

    梳理下实现步骤
    我们需要实现这个底部菜单导航,就需要有底部菜单的那一排图标按钮。图标按钮是固定在一个工具栏 “bar” 上面。然后呢,需要分别需要有按钮对应的界面,就是说按钮有多少个,那么界面需要对应的有多少个。我们来一个清单列表:

    按钮图标区域。由于展示的方式都是一样的,我们需要有一个单独的控件,循环出来就好。
    工具栏区域。用于展示按钮图标,并且能固定在底部。
    首页。用于将工具栏放入界面中,并且将按钮对应的界面作为它的子元素存放于其中。
    不同的按钮对应的界面。在我们点击的图标按钮的时候,展示不同的界面。
    我们底部的按钮是不会刷新的,界面会刷新,如何实现?

    我们界面展示区域分为两块,一块展示底部的工具栏,一块展示页面。下面代码实现:

    return new MaterialApp(
            home: new Scaffold(
              body: new Center(
                  child: _currentPage   // 动态的展示我们当前的页面
              ),
              bottomNavigationBar: bottomNavigationBar,   // 底部工具栏
            ),
    
          theme: new ThemeData(
            primarySwatch: Colors.blue,   // 设置主题颜色
          ),
    
        );

    具体实现
    第一步:创建一个 flutter 工程
    可以按照工程目录图中的结构,将对应的文件建好。

    第二步:修改 main.dart。
    main.dart 是我们程序的入口。就类似于 Java、C 中的 main() ,作为一个程序的入口。我们将 main.dart 作为程序的启动入口,就不做过多的操作,只是指定去加载我们的首页(index.dart)。

    main.dart

    import 'package:flutter/material.dart';
    import 'package:flutter_demo/index/index.dart';         // 导入index.dart
    
    // 这里为入口函数
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          home: new Index(),     // 指定去加载 Index页面。
        );
      }
    }

    第三步:创建 NavigationIconView。 
    正如前面说的,我们底部的按钮区域展示的图标加上文字是固定格式,所以将这一部分抽取出来,作为一个公共的 class,方便界面程序维护。

    navigation_icon_view.dart

    import 'package:flutter/material.dart';
    
    // 创建一个 Icon 展示控件
    class NavigationIconView {
    
      // 创建两个属性,一个是 用来展示 icon, 一个是动画处理
      final BottomNavigationBarItem item;
      final AnimationController controller;
    
      // 类似于 java 中的构造方法
      // 创建 NavigationIconView 需要传入三个参数, icon 图标,title 标题, TickerProvider
      NavigationIconView({Widget icon, Widget title, TickerProvider vsync}):
        item = new BottomNavigationBarItem(
          icon: icon,
          title: title,
        ),
        controller = new AnimationController(
          duration: kThemeAnimationDuration,    // 设置动画持续的时间
          vsync: vsync                          // 默认属性和参数
        );
    }

    第四步:创建 Index 
    这一步就比较重要了,因为我们需要在这个界面上面去布局,以及实现点击按钮图标之后,有事件触发。正因为我们需要有事件触发,所以创建一个带有状态的 Widget(StatefulWidget)。下面的代码注释给的很详细了,可以仔细看。

    index.dart

    import 'package:flutter/material.dart';
    import 'package:flutter_demo/NoticePage/notice_page.dart';
    import 'package:flutter_demo/home/home_page.dart';
    import 'package:flutter_demo/idea/idea_page.dart';
    import 'package:flutter_demo/market/market_page.dart';
    import 'package:flutter_demo/my/my_page.dart';
    
    import 'navigation_icon_view.dart'; // 如果是在同一个包的路径下,可以直接使用对应的文件名
    
    // 创建一个 带有状态的 Widget Index
    class Index extends StatefulWidget {
    
      //  固定的写法
      @override
      State<StatefulWidget> createState()  => new _IndexState();
    }
    
    // 要让主页面 Index 支持动效,要在它的定义中附加mixin类型的对象TickerProviderStateMixin
    class _IndexState extends State<Index> with TickerProviderStateMixin{
    
      int _currentIndex = 0;    // 当前界面的索引值
      List<NavigationIconView> _navigationViews;  // 底部图标按钮区域
      List<StatefulWidget> _pageList;   // 用来存放我们的图标对应的页面
      StatefulWidget _currentPage;  // 当前的显示页面
    
      // 定义一个空的设置状态值的方法
      void _rebuild() {
        setState((){});
      }
    
      @override
      void initState() {
        super.initState();
    
        // 初始化导航图标
        _navigationViews = <NavigationIconView>[
          new NavigationIconView(icon: new Icon(Icons.assessment), title: new Text("首页"), vsync: this), // vsync 默认属性和参数
          new NavigationIconView(icon: new Icon(Icons.all_inclusive), title: new Text("想法"), vsync: this),
          new NavigationIconView(icon: new Icon(Icons.add_shopping_cart), title: new Text("市场"), vsync: this),
          new NavigationIconView(icon: new Icon(Icons.add_alert), title: new Text("通知"), vsync: this),
          new NavigationIconView(icon: new Icon(Icons.perm_identity), title: new Text("我的"), vsync: this),
        ];
    
        // 给每一个按钮区域加上监听
        for (NavigationIconView view in _navigationViews) {
          view.controller.addListener(_rebuild);
        }
    
        // 将我们 bottomBar 上面的按钮图标对应的页面存放起来,方便我们在点击的时候
        _pageList = <StatefulWidget>[
          new HomePage(),
          new IdeaPage(),
          new MarketPage(),
          new NoticePage(),
          new MyPage()
        ];
        _currentPage = _pageList[_currentIndex];
      }
    
      @override
      Widget build(BuildContext context) {
    
        // 声明定义一个 底部导航的工具栏
        final BottomNavigationBar bottomNavigationBar = new BottomNavigationBar(
          items: _navigationViews
              .map((NavigationIconView navigationIconView) => navigationIconView.item)
              .toList(),  // 添加 icon 按钮
          currentIndex: _currentIndex,  // 当前点击的索引值
          type: BottomNavigationBarType.fixed,    // 设置底部导航工具栏的类型:fixed 固定
          onTap: (int index){   // 添加点击事件
            setState((){    // 点击之后,需要触发的逻辑事件
              _navigationViews[_currentIndex].controller.reverse();
              _currentIndex = index;
              _navigationViews[_currentIndex].controller.forward();
              _currentPage = _pageList[_currentIndex];
            });
          },
        );
    
        return new MaterialApp(
            home: new Scaffold(
              body: new Center(
                  child: _currentPage   // 动态的展示我们当前的页面
              ),
              bottomNavigationBar: bottomNavigationBar,   // 底部工具栏
            ),
    
          theme: new ThemeData(
            primarySwatch: Colors.blue,   // 设置主题颜色
          ),
    
        );
      }
    
    }

    第四步:创建页面 
    前面的步骤都是在搭建我们的基础,现在是做展示界面。由于不同的界面,对应的源码都是和下面的是一样的,只是 class 的名字不一样,就都可以使用同样的模版复制过去就有可以了。

    home_page.dart

    import 'package:flutter/material.dart';
    
    class HomePage extends StatefulWidget{
      @override
      State<StatefulWidget> createState() => new _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('首页'),
              actions: <Widget>[
                new Container()
              ],
            ),
            body: new Center(
              child: null,
            ),
          ),
        );
      }
    }
    
    
    
    
    idea_page.dart
    import 'package:flutter/material.dart';
    
    class IdeaPage extends StatefulWidget{
      @override
      State<StatefulWidget> createState() => new _IdeaPageState();
    }
    class _IdeaPageState extends State<IdeaPage> {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('想法'),
              actions: <Widget>[
                new Container()
              ],
            ),
            body: new Center(
              child: null,
            ),
          ),
        );
      }
    }

    market_page.dart

    import 'package:flutter/material.dart';
    class MarketPage extends StatefulWidget{
      @override
      State<StatefulWidget> createState() => new _MarketPageState();
    }
    class _MarketPageState extends State<MarketPage> {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text('市场'),
              // 后面的省略
              // ......
            )
          ),
        );
      }
    
    }
  • 相关阅读:
    串口调适
    取出不重复的6个数
    个人Windows 10必备软件以及浏览器必装插件等
    合肥工业大学宣城校区2018年-2019年第一学期(大三上学期)物联网工程专业资料汇总(含课件、个人实验报告、实验代码、课设报告等)
    合肥工业大学宣城校区2019年-2020年第二(大三下)学期物联网工程专业资料汇总(含课件、个人实验报告、实验代码、课设报告等)
    合肥工业大学宣城校区2020年-2021年第一(大四上)学期物联网工程专业资料汇总(含课件、个人实验报告、实验代码、课设报告等)
    软件工程-单元测试-计算机测试-复习札记
    8086汇编计算分段函数值
    C语言是开源的吗?C++是开源的吗?C语言、C++是两个开源的标准,而不是开源软件或其它
    合肥工业大学编译原理实验LR(1)文法分析完整Scala实现代码(Java封装GUI)与测试数据
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/10130290.html
Copyright © 2011-2022 走看看