美文网首页
2019-12-23

2019-12-23

作者: 涐罙擁那嗰懂涐 | 来源:发表于2019-12-23 16:01 被阅读0次

    # 前言

    Google推出flutter这样一个新的高性能跨平台(Android,ios)快速开发框架之后,被业界许多开发者所关注。我接触了flutter之后,确实发现它的一些优越性。

    今天我来给大家分享的是底部导航功能的实现。废话不多说开工啦。

    # 学到知识?

    1.拆分组件

    2.构建简单布局

    3.创建底部导航

    最终效果如下:

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191223150539771.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE1MDU3MjEz,size_16,color_FFFFFF,t_70)

    这也是我刚开始接触的,学到的知识,如有错误,请指教。

    # 搭建布局

    ## 1. 绘制布局视图

    不可缺少的,main() 相当于入口方法,是必须要实现的。

    ```javascript

    void main() => runApp(MyApp());

    ```

    首先要实现MyApp

    ```javascript

    class MyApp extends StatelessWidget {

      // This widget is the root of your application.

      @override

      Widget build(BuildContext context) {

        return MaterialApp(

          title: '首页',

          theme: ThemeData(

            // This is the theme of your application.

            //

            // Try running your application with "flutter run". You'll see the

            // application has a blue toolbar. Then, without quitting the app, try

            // changing the primarySwatch below to Colors.green and then invoke

            // "hot reload" (press "r" in the console where you ran "flutter run",

            // or simply save your changes to "hot reload" in a Flutter IDE).

            // Notice that the counter didn't reset back to zero; the application

            // is not restarted.

            primarySwatch: Colors.blue,

          ),

          home: new Scaffold(

            body: new Center(

              child: new MainTab(),

            ),

          ),

        );

      }

    }

    ```

    这里有些同学或许对StatelessWidget 和 StatefulWidget 分不太清楚。或许有疑问他们两者区别在哪里?

    我们先查看StatelessWidget 的源码:

    ```javascript

    abstract class StatelessWidget extends Widget {

      /// Initializes [key] for subclasses.

      const StatelessWidget({ Key key }) : super(key: key);

      /// Creates a [StatelessElement] to manage this widget's location in the tree.

      ///

      /// It is uncommon for subclasses to override this method.

      @override

      StatelessElement createElement() => StatelessElement(this);

      /// Describes the part of the user interface represented by this widget.

      ///

      /// The framework calls this method when this widget is inserted into the

      /// tree in a given [BuildContext] and when the dependencies of this widget

      /// change (e.g., an [InheritedWidget] referenced by this widget changes).

      ///

      /// The framework replaces the subtree below this widget with the widget

      /// returned by this method, either by updating the existing subtree or by

      /// removing the subtree and inflating a new subtree, depending on whether the

      /// widget returned by this method can update the root of the existing

      /// subtree, as determined by calling [Widget.canUpdate].

      ///

      /// Typically implementations return a newly created constellation of widgets

      /// that are configured with information from this widget's constructor and

      /// from the given [BuildContext].

      ///

      /// The given [BuildContext] contains information about the location in the

      /// tree at which this widget is being built. For example, the context

      /// provides the set of inherited widgets for this location in the tree. A

      /// given widget might be built with multiple different [BuildContext]

      /// arguments over time if the widget is moved around the tree or if the

      /// widget is inserted into the tree in multiple places at once.

      ///

      /// The implementation of this method must only depend on:

      ///

      /// * the fields of the widget, which themselves must not change over time,

      ///  and

      /// * any ambient state obtained from the `context` using

      ///  [BuildContext.dependOnInheritedWidgetOfExactType].

      ///

      /// If a widget's [build] method is to depend on anything else, use a

      /// [StatefulWidget] instead.

      ///

      /// See also:

      ///

      ///  * [StatelessWidget], which contains the discussion on performance considerations.

      @protected

      Widget build(BuildContext context);

    }

    ```

    再看StatefulWidget 的源码:

    ```javascript

    abstract class StatefulWidget extends Widget {

      /// Initializes [key] for subclasses.

      const StatefulWidget({ Key key }) : super(key: key);

      /// Creates a [StatefulElement] to manage this widget's location in the tree.

      ///

      /// It is uncommon for subclasses to override this method.

      @override

      StatefulElement createElement() => StatefulElement(this);

      /// Creates the mutable state for this widget at a given location in the tree.

      ///

      /// Subclasses should override this method to return a newly created

      /// instance of their associated [State] subclass:

      ///

      /// ```dart

      /// @override

      /// _MyState createState() => _MyState();

      /// ```

      ///

      /// The framework can call this method multiple times over the lifetime of

      /// a [StatefulWidget]. For example, if the widget is inserted into the tree

      /// in multiple locations, the framework will create a separate [State] object

      /// for each location. Similarly, if the widget is removed from the tree and

      /// later inserted into the tree again, the framework will call [createState]

      /// again to create a fresh [State] object, simplifying the lifecycle of

      /// [State] objects.

      @protected

      State createState();

    }

    ```

    我们发现最大的不同是:**createState**这个方法。就是可以改变状态。

    那么它们的区别就是:__是否可以状态改变(在App中则是交互---按钮的响应、页面刷新等)__

    ## 2. 搭建底部导航

    ```javascript

    class MainTab extends StatefulWidget{

      @override

      State<StatefulWidget> createState() {

        return new _MainBottomTab();

      }

    }

    class _MainBottomTab extends State<MainTab>{

      int _currentIndex = 0;

      List<Widget> pages = [MainPage(),FindPage(),MinePage()];

      @override

      Widget build(BuildContext context) {

        return new Scaffold(

          body: pages[_currentIndex],

          bottomNavigationBar: new BottomNavigationBar(

            items: [

              new BottomNavigationBarItem(icon: _currentIndex == 0 ? new Image.asset(Images.main,width: 24,height: 24,) : new Image.asset(Images.main_nor,width: 24,height: 24,),

              title: Text(

                "首页",

              )),

              new BottomNavigationBarItem(icon: _currentIndex == 1 ? new Image.asset(Images.find,width: 24,height: 24,) : new Image.asset(Images.find_nor,width: 24,height: 24,),

              title: Text(

                "发现",

              )),

              new BottomNavigationBarItem(icon: _currentIndex == 2 ? new Image.asset(Images.me,width: 24,height: 24,) : new Image.asset(Images.me_nor,width: 24,height: 24,),

              title: Text(

                "我的",

              )),

            ],

            fixedColor: Colors.blue,

            currentIndex: _currentIndex,

            onTap: (int index){

              setState(() {

                _currentIndex = index;

              });

            },

            type: BottomNavigationBarType.fixed,

            selectedFontSize: 12,

          ),

        );

      }

    }

    ```

    Scaffold 是flutter常用的布局控件之一。为什么用Scaffold?

    因为Scaffold有一个默认的bottomNavigationBar的属性。bottomNavigationBar就是底部导航的关键。我在这里给它一个bottomNavigationBar,并在这个bottomNavigationBar放了三个BottomNavigationBarItem。每个BottomNavigationBarItem就是底部的一个导航按钮。

    不仅如此,BottomNavigationBar还为我们提供了一个currentIndex的属性,它代表了当前再BottomNavigationBarItem中被选中的index。源码如下:

    ```javascript

    /// The index into [items] for the current active [BottomNavigationBarItem].

      final int currentIndex;

    ```

    BottomNavigationBar还提供了一个onTap方法。

    ```javascript

    /// Called when one of the [items] is tapped.

      ///

      /// The stateful widget that creates the bottom navigation bar needs to keep

      /// track of the index of the selected [BottomNavigationBarItem] and call

      /// `setState` to rebuild the bottom navigation bar with the new [currentIndex].

      final ValueChanged<int> onTap;

    ```

    当底部导航的一个BottomNavigationBarItem被点击时,它会调用此方法,并传入当前BottomNavigationBarItem的index值,这样就能改变焦点到当前的index上的BottomNavigationBarItem了。

    # 创建切换页面

    我们的App不只是单页面切换,我需要切换不同的页面。

    下面我们来创建不同的页面

    创建页面如下:

    ```javascript

    import 'package:flutter/material.dart';

    class MainPage extends StatelessWidget{

      @override

      Widget build(BuildContext context) {

        return MaterialApp(

          home: new Scaffold(

            appBar: new AppBar(

              title: Text("首页"),

            ),

            body: new Center(

              child: Text("我的首页部分"),

            ),

          ),

          );

      }

    }

    ```

    如以上方式创建。

    # 显示到页面上,并可以切换

    由于我们是通过currentIndex这个变量来控制跳转的,页面要想同步也必须依赖于这个变量。这里我们使用一个List<Widget>来与items对应。

    ```javascript

    List<Widget> pages = [MainPage(),FindPage(),MinePage()];

    ```

    然后在_MainBottomTab的build中body中实现

    ```javascript

    body: pages[_currentIndex],

    ```

    现在基本可以显示。但是切换的时候需要改变items选中的图片,怎么办?

    我们还是利用之前定义的_currentIndex,来和选中 BottomNavigationBarItem进行比较即可。

    ```javascript

    new BottomNavigationBarItem(icon: _currentIndex == 0 ? new Image.asset(Images.main,width: 24,height: 24,) : new Image.asset(Images.main_nor,width: 24,height: 24,),

              title: Text(

                "首页",

              )),

    ```

    在我医用图片的时候发现图片一直显示不出来。现在推荐一种方法。

    在根目录下创建resource文件,吧图片放到这个文件中。然后在pubspec.yaml 中添加所需要的图片路径。

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191223154822908.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE1MDU3MjEz,size_16,color_FFFFFF,t_70)

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191223154755673.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE1MDU3MjEz,size_16,color_FFFFFF,t_70)

    然后在需要的地方用

    ```javascript

    Image.asset("resource/images/main.png",width: 24,height: 24,)

    ```

    这样就可以正常显示图片了。如有其它方法还望告知,我也是学习中。大家一起努力。

    如有问题可以私信我哦。

    相关文章

      网友评论

          本文标题:2019-12-23

          本文链接:https://www.haomeiwen.com/subject/ryoznctx.html