flutter学习七:顶部导航栏TabBar切换子布局FutureBuilder、shared_preferences、ExpansionTile、RefreshIndicat
实现顶部导航栏TabBar+TabBarView切换子布局,因为也想写FutureBuilder、shared_preferences、ExpansionTile、RefreshIndicat文章,索性一起写了,顶部导航切换的四个子布局分别是:FutureBuilderPage:FutureBuilder的使用;SharedPreferencesPage:shared_preference...
实现顶部导航栏TabBar+TabBarView切换子布局,因为也想写FutureBuilder、shared_preferences、ExpansionTile、RefreshIndicat文章,索性一起写了,顶部导航切换的四个子布局分别是:
- FutureBuilderPage:FutureBuilder的使用;
- SharedPreferencesPage:shared_preferences本地数据的存储相当于Android的SharedPreferences;
- ExpansionTilePage:ExpansionTile二级列表的展开收缩;
- 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网络请求
更多推荐
所有评论(0)