实现顶部导航栏TabBar+TabBarView切换子布局,因为也想写FutureBuilder、shared_preferences、ExpansionTile、RefreshIndicat文章,索性一起写了,顶部导航切换的四个子布局分别是:

  1. FutureBuilderPage:FutureBuilder的使用;
  2. SharedPreferencesPage:shared_preferences本地数据的存储相当于Android的SharedPreferences;
  3. ExpansionTilePage:ExpansionTile二级列表的展开收缩;
  4. RefreshPage:ListView的水平、垂直布局,再次基础上加上RefreshIndicator下拉刷新、下拉加载更多;

效果图如下(看顶部导航栏选中的对比前面的四点即可理解):

     

                       

             

                          

 

步骤如下:

 

一. MinePage实现顶部导航栏TabBar+TabBarView切换不同子布局

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/pages/RefreshPage.dart';

import 'ExpansionTilePage.dart';
import 'FutureBuilderPage.dart';
import 'SharedPreferencesPage.dart';

//我的包含顶部导航栏
class MinePage extends StatefulWidget {
  _MinePageState createState() => _MinePageState();
}

class _MinePageState extends State<MinePage>
    with SingleTickerProviderStateMixin {
  TabController tabController;

  // 初始化方法
  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 4, vsync: this);
  }

  // 销毁方法
  @override
  void dispose() {
    tabController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        //标题居中
        centerTitle: true,
        //标题栏背景色
        backgroundColor: Colors.blue,
        //设置没有返回按钮
        automaticallyImplyLeading: false,
        title: TabBar(
          //选中的颜色
          labelColor: Colors.white,
          //选中的样式
          labelStyle: TextStyle(fontSize: 15),
          //未选中的颜色
          unselectedLabelColor: Colors.black,
          //未选中的样式
          unselectedLabelStyle: TextStyle(fontSize: 12),
          //是否可以滚动
          isScrollable: true,
          //指示器颜色
          indicatorColor: Colors.white,
          //指示器高度
          indicatorWeight: 1,
          //底部指示器的padding
          indicatorPadding: EdgeInsets.zero,
          //指示器大小计算方式,TabBarIndicatorSize.label跟文字等宽,TabBarIndicatorSize.tab跟每个tab等宽
          indicatorSize: TabBarIndicatorSize.tab,
          //每个label的padding值
          labelPadding: EdgeInsets.all(10),
          //内容
          tabs: <Widget>[
            Tab(icon: Icon(Icons.flag),text:'FutureBuilder'),
            Tab(icon: Icon(Icons.share),text:'shared_preferences'),
            Tab(icon: Icon(Icons.filter_vintage),text:'ExpansionTile'),
            Tab(icon: Icon(Icons.layers),text:'Refresh'),
          ],
          controller: tabController,
        ),
      ),
      body: Container(
        child: TabBarView(
          children: <Widget>[
            FutureBuilderPage(),
            SharedPreferencesPage(),
            ExpansionTilePage(),
            RefreshPage(),
          ],
          controller: tabController,
        ),
      ),
    );
  }
}

 

二.  FutureBuilderPage:FutureBuilder的使用(效果图:效果图中第一行)

1.首先得准备一个url,可以请求得到数据的

2. 具体代码如下:

import 'dart:convert';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/bean/BaseBean.dart';
import 'package:http/http.dart' as http;

//FutureBuilder的使用
class FutureBuilderPage extends StatefulWidget {
  @override
  _FutureBuilderPageState createState() => _FutureBuilderPageState();
}

class _FutureBuilderPageState extends State<FutureBuilderPage> {
  String showResult = '';

  Future<BaseBean> getData() async {
    final response = await http.get(
        'http://192.168.0.224:8080/eolinker_os/Mock/simple?projectID=2&uri=http://www.mcl.net:8888/api/Test');
    Utf8Decoder utf8decoder = Utf8Decoder(); //中文乱码
    var result = json.decode(utf8decoder.convert(response.bodyBytes));
    return BaseBean.from(result);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: FutureBuilder<BaseBean>(
          future: getData(),
          builder: (BuildContext context, AsyncSnapshot<BaseBean> snapshot) {
            //连接状态
            switch (snapshot.connectionState) {
              //无连接
              case ConnectionState.none:
                return new Container(
                  alignment: Alignment.center,
                  child: Text("没有连接"),
                );

              //等待
              case ConnectionState.waiting:
                return new Center(
                  child: new CircularProgressIndicator(),
                );

              //正在执行
              case ConnectionState.active:
                return new Container(
                  alignment: Alignment.center,
                  child: Text("正在执行ing"),
                );

              //执行完成
              case ConnectionState.done:
                //出错了,显示出错信息
                if (snapshot.hasError) {
                  return new Container(
                    alignment: Alignment.center,
                    child: Text(
                      '${snapshot.error}',
                      style: TextStyle(color: Colors.red),
                    ),
                  );
                } else {
                  return new Container(
                    margin: EdgeInsets.only(top: 20),
                    alignment: Alignment.center,
                    child: Column(
                      children: <Widget>[
                        Text('status:${snapshot.data.status}'),
                        Text('msg:${snapshot.data.msg}'),
                        Text('data:${snapshot.data.data}')
                      ],
                    ),
                  );
                }
            }
          },
        ),
      ),
    );
  }
}

 

三. SharedPreferencesPage:本地数据存取简单相当于Android中的SharedPreferences(效果图:效果图中第二行),存储的key固定,存储TextField输入的内容,获取key的内容,数据类型要对应上。类型有挺多,具体看api:

1.添加包:

2.代码如下:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

//shared_preferences本地数据存取简单、异步、持久化
class SharedPreferencesPage extends StatefulWidget {
  _SharedPreferencesPageState createState() => _SharedPreferencesPageState();
}

class _SharedPreferencesPageState extends State<SharedPreferencesPage> {
  String putStr = ''; //存的数据
  String getStr = ''; //取的数据
  String strKey = 'strKey'; //存或取的key
  //判断输入的内容是否为空
  static bool _stateSp = false;

  //用于弹框消失时
  static BuildContext context1;

  //监听输入框的文字变化
  static TextEditingController _spController = new TextEditingController();

  //账号输入框样式
  static Widget buildAccountTextFied(TextEditingController controller) {
    return TextField(
      //键盘的样式
      keyboardType: TextInputType.text,
      //监听
      controller: controller,
      //最大长度
      maxLength: 30,
      //颜色跟hintColor
      //最大行数
      maxLines: 1,
      //是否自动更正
      autocorrect: true,
      //是否自动化对焦
      autofocus: false,
      //文本对齐方式
      textAlign: TextAlign.start,
      //输入文本的样式
      style: TextStyle(fontSize: 20, color: Colors.black),
      decoration: InputDecoration(
        //聚焦时才显示,颜色跟hintColor
        hintText: '请输入存储数据',
      ),
    );
  }

  //账号、密码输入框
  Widget textSection = new Container(
    padding: const EdgeInsets.only(left: 32, right: 32),
    child: new Column(
      //主轴Flex的值
      mainAxisSize: MainAxisSize.max,
      //MainAxisAlignment:主轴方向上的对齐方式,会对child的位置起作用
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        buildAccountTextFied(_spController),
      ],
    ),
  );

  //封装好的button控件
  Widget buttons(String name) {
    return new Container(
      alignment: Alignment.centerLeft,
      height: 50,
      margin: EdgeInsets.all(32),
      color: Colors.red,
      child: new Text(
        name,
        style: TextStyle(fontSize: 20, color: Colors.white),
      ),
    );
  }

  static Widget getDialog(String msg) {
    return new AlertDialog(
      title: new Text(
        '温馨提示',
        style: new TextStyle(color: Colors.red[250], fontSize: 18),
      ),
      content: new Text(
        msg,
        style: new TextStyle(color: Colors.red, fontSize: 20),
      ),
      actions: <Widget>[
        new FlatButton(
            //扁平的button
            onPressed: () {
              Navigator.of(context1).pop(); //弹窗消失
            },
            child: new Text(
              '取消',
              style: new TextStyle(color: Colors.blue, fontSize: 18),
            )),
        new RaisedButton(
            //凸起的button
            onPressed: () {
              Navigator.of(context1).pop(); //弹窗消失
            },
            child: new Text(
              '确定',
              style: new TextStyle(color: Colors.blue, fontSize: 18),
            ))
      ],
    );
  }

  //校验存储的数据是否为null
  static void _checkSp() {
    if (_spController.text.isNotEmpty) {
      _stateSp = true;
    } else {
      _stateSp = false;
    }
  }

  //存数据
  _putSp() async {
    SharedPreferences sp = await SharedPreferences.getInstance();
    //把有修改的视图重新绘制一遍
    setState(() {
      putStr = _spController.text.toString(); //获取TextField输入的内容
    });
    //保存String类型,还有int、Double、List<String>、Bool(动态类型)
    await sp.setString(strKey, putStr);
  }

  //获取数据
  _getSp() async {
    SharedPreferences sp = await SharedPreferences.getInstance();
    //把有修改的视图重新绘制一遍
    setState(() {
      /*
      * 获取String类型,还有int、Double、List<String>、Bool(动态类型);
      * 获取动态类型时:sp.getBool(strKey) as String需要在后面加上“as 具体类型”
      * */
      getStr = sp.getString(strKey).toString();
      print('取出来的:' + getStr);
    });
  }

  @override
  Widget build(BuildContext context) {
    context1 = context;
    return Scaffold(
        body: Container(
      color: Colors.white,
      child: new ListView(
        children: <Widget>[
          textSection,
          RaisedButton(
              onPressed: () {
                _checkSp();
                if (_stateSp) {
                  //存储的数据不为null,那么存储
                  _putSp();
                } else {
                  //存储数据为null,那么弹窗提示用户
                  showDialog(
                    context: context1,
                    barrierDismissible: true, //点击弹窗外部是否消失
                    child: getDialog('存储的数据不能为空!'),
                  );
                }
              },
              child: Text('存数据')),
          buttons('存储的内容:' + putStr),
          RaisedButton(onPressed: _getSp, child: Text('获取数据')),
          buttons('获取的结果:' + getStr),
        ],
      ),
    ));
  }
}

 

四. ExpansionTilePage:可展开的二级列表(效果图:效果图中第三行)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

//可展开的二级列表
class ExpansionTilePage extends StatefulWidget {
  _ExpansionTilePageState createState() => _ExpansionTilePageState();
}

class _ExpansionTilePageState extends State<ExpansionTilePage> {
  var CITY_NAME = {
    '贺州': ['1', '2', '3'],
    '桂林': ['1', '2', '3'],
    '梧州': ['1', '2', '3'],
    '广州': ['1', '2', '3'],
  };

  List<Widget> _buildList() {
    List<Widget> widgets = [];
    //将数据装到widget中
    CITY_NAME.keys.forEach((key) {
      widgets.add(_item(key, CITY_NAME[key]));
    });
    return widgets;
  }

  //一级的布局,及关联二级
  Widget _item(String city, List<String> subCities) {
    return ExpansionTile(
      title: Text(
        city,
        style: TextStyle(color: Colors.black, fontSize: 20),
      ),
      children: subCities.map((subCities) => _buildSub(subCities)).toList(),
    );
  }

  //二级的布局
  Widget _buildSub(String subCities) {
    return FractionallySizedBox(
      widthFactor: 1,
      child: Container(
//        color: Colors.lightBlueAccent,
        height: 50,
        margin: EdgeInsets.only(bottom: 5),
        alignment: Alignment.centerLeft,
        //装饰
        decoration: BoxDecoration(color: Colors.lightBlueAccent),
        child: Text(
          subCities,
          style: TextStyle(fontSize: 18),
        ),
      ),
    );
  }

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

 

五. RefreshPage:ListView的显示方向垂直、水平,RefreshIndicator下拉刷新上拉加载更多:(效果图:效果图中第四行)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

//上下拉刷新(不满屏时没有操作)
class RefreshPage extends StatefulWidget {
  _RefreshPageState createState() => _RefreshPageState();
}

class _RefreshPageState extends State<RefreshPage> {
  List<String> chainList = [
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    '10',
  ];
  ScrollController _scrollController = ScrollController();

  void initState() {
    //把滚动控制器加入到监听里面,滚动到大后就加载更多
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        _loadData();
      }
    });
    super.initState();
  }

  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  //上拉加载更多,延时2秒后
  _loadData() async {
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      List<String> list =
          List<String>.from(chainList); //此时,list里面复制了一份chainList
      list.addAll(chainList); //list再加一份chainList(此时list有两份chainList)
      chainList = list; //更改chainList(总是比原先多一份)
    });
  }

  //下拉刷新时,延时2秒后执行
  Future<Null> _handleRefresh() async {
    await Future.delayed(Duration(seconds: 2));
    //把有修改的视图重新绘制一遍,这里chainList变了
    setState(() {
      //把chainList变成新的chainList(元素倒置第一个与最后一个对换,以此类推)
      chainList = chainList.reversed.toList();
    });
    return null;
  }

  //数据源循环每个元素
  List<Widget> getList() {
    return chainList.map((item) => getListWidget(item)).toList();
  }

  //每个子widget的样式
  static Widget getListWidget(String str) {
    return Container(
      height: 50,
      width: 30,
      color: Colors.lightBlueAccent,
      alignment: Alignment.center,
      margin: EdgeInsets.all(10),
      child: Text(
        str,
        style: TextStyle(color: Colors.white, fontSize: 18),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RefreshIndicator(
        //下拉刷新
        onRefresh: _handleRefresh,
        child: ListView(
          //滚动的方向,可水平、垂直
          scrollDirection: Axis.vertical,
          //是否倒序显示内容(默认false,改成true时,会从底部开始布局的)
          reverse: false,
          //距离
          padding: EdgeInsets.all(15),
          //滑动监听,用于上拉加载更多,监听滑动的距离来执行相应的操作
          controller: _scrollController,
          //下拉刷新
          children: getList(),
        ),
      ),
    );
  }
}

 

到目前为止,flutter的布局暂时先写到这里,后期有新的学习,会继续写相关文章的,接下来学习Flutter与Android混合开发,从flutter学习一到七及没有出现在文章的代码,全部上传到我的代码云上了。

Gitee地址:https://gitee.com/mo19940322/flutter_mo 

上一篇:flutter学习六:实现http网络请求

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐