当前位置: 移动技术网 > 移动技术>移动开发>Android > Flutter实现底部菜单导航

Flutter实现底部菜单导航

2019年07月29日  | 移动技术网移动技术  | 我要评论

简介

现在我们的 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('市场'),
   // 后面的省略
   // ......
  )
  ),
 );
 }

}

剩下的界面都是一样子的了,就不贴出来了,都是复制一下,改了一个类名,和显示的文字而已。

第五步:启动测试

到此我们就完成了,可以启动程序,看下效果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网