为什么选择Flutter
uni-app可实现跨平台开发,目前用到的是小程序开发,同事推荐学习Flutter,开始以为Flutter也可以是实现跨小程序开发,结果只是移动端和H5开发,但是对于移动端开发人员来说还是很不错的选择。
2020.03.02
按着视频教程和文档学习
组件
自定义 Widget、Center 组件、Text 组件、MaterialApp 组件、Scaffold 组件
Text 组件
该 widget 可让创建一个带格式的文本。
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Text(
'我是一个文本,你是个傻呀呀呀呀呀呀呀啊呀呀',
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
maxLines: 2, //文字显示最大行数
// textDirection: TextDirection.rtl,//文本方向
// textScaleFactor: 1.0,//文字显示倍数
style:TextStyle( //文字样式设置
fontSize: 30.0,
fontWeight: FontWeight.w800,
color: Colors.blue,
decoration: TextDecoration.lineThrough,
decorationColor: Colors.white,
decorationStyle: TextDecorationStyle.dashed,//虚线
wordSpacing: 3.0,
letterSpacing: 1.5,
fontStyle: FontStyle.italic
),
),
width:300.0,
height: 300.0,
decoration: BoxDecoration( //文字装饰线
color: Colors.yellow,
border: Border.all(
color: Colors.red,
width: 2.0,
),
borderRadius: BorderRadius.all(
//Radius.circular(150) //圆形
Radius.circular(8.0)
),
//borderRadius: BorderRadius.circular(20)
),
// margin: EdgeInsets.all(20), //
padding: EdgeInsets.all(20),
// padding: EdgeInsets.fromLTRB(10, 20, 40, 5),
// transform: Matrix4.translationValues(100, 30, 0),
// transform: Matrix4.rotationZ(0.3),
// transform:Matrix4.diagonal3Values(1.2, 1, 1),
alignment: Alignment.center,//配置元素位置
),
);
}
}
Container 组件
容器组件,相当于 div,布局组件。可创建矩形视觉元素。
MaterialApp组件
Scaffold组件
图片组件
引入远程图片Image.network
普通引入图片
child: Image.network(
'https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/2/1709a01b029a6ef0~tplv-t2oaga2asx-image.image',
alignment: Alignment.topLeft,
// color: Colors.blue,
// colorBlendMode: BlendMode.darken,
fit: BoxFit.contain,
repeat: ImageRepeat.noRepeat,
width: 200,
height: 200,
),
实现圆角以及实现圆形图片:
decoration: BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.circular(150),
image: DecorationImage(
image: NetworkImage('https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/2/1709a01b029a6ef0~tplv-t2oaga2asx-image.image'),
fit: BoxFit.cover
)
),
推荐使用这一种
child: ClipOval(
child: Image.network(
'https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/2/1709a01b029a6ef0~tplv-t2oaga2asx-image.image',
width:100.0,
height: 100.0,
fit: BoxFit.cover,
),
),
引入本地图片Image.asset
- 新增images文件夹:
images文件夹中包括2.0x(必须)、3.0x(必须)、4.0x、最外层的图片,如下图所示:
- 打开
pubspec.yaml声明一下添加的图片文件,注意要配置对,如下图所示:
- 在代码中使用
child: Container(
child: Image.asset(
'images/2.png' ,
fit:BoxFit.cover
),
2020.03.03
图标组件
new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null 会禁用 button
),
ListView列表组件
ListView列表参数
| 名称 | 类型 | 说明 |
|---|---|---|
| scrollDirection | Axis | Axis.horizontal 水平列、Axis.vertical 垂直列表 |
| padding | EdgeInsetsGeometry | 内边距 |
| resolve | bool | 组件反向排序 |
| children | List | 列表元素 |
ListViewListView.builder
默认垂直列表(图文列表)
水平列表
scrollDirection:Axis.horizontal
矩阵式列表
新闻列表 :
class HomeContent extends StatelessWidget{
// 自定义方法 加_ 私有方法
// 2.获取返回的listData数据,通过map遍历渲染数据
List<Widget> _getData(){
var tempList = listData.map((v){ //map返回结构是(xx,xx)
return ListTile(
leading: Image.network(v['imageUrl']),
title: Text(v['title']),
subtitle: Text(v['author']),
trailing:Icon(Icons.arrow_forward_ios,color:Colors.red),
);
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
//调用遍历的方法
return ListView(
children: this._getData(),//调用遍历的方法
);
}
}
动态列表
import './res/listData.dart';
class HomeContent extends StatelessWidget{
//把遍历的内容提取出来
Widget _getListData(context,index){
return ListTile(
leading: Image.network(listData[index]['imageUrl']),
title: Text(listData[index]['title']),
subtitle: Text(listData[index]['author']),
trailing:Icon(Icons.arrow_forward_ios,color:Colors.orange),
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView.builder(
itemCount: listData.length,
itemBuilder: this._getListData,//此处是赋值
);
}
}
ListTile组件
this.leading,
this.title,
this.subtitle,
this.trailing,
CircleAvatar组件
头像组件
ListTile(
// leading: Image.network('https://www.itying.com/images/flutter/2.png',fit: BoxFit.cover,),
leading:CircleAvatar(
backgroundImage:NetworkImage(value['imageUrl'])
),
title: Text(value['title']),
subtitle: Text(value['description'],maxLines: 2,overflow:TextOverflow.ellipsis),
),
GridView组件实现网格布局
| 名称 | 类型 | 说明 |
|---|---|---|
| scrollDirection | Axis | 滚动方法 |
| padding | EdgeInsetsGeometry | 内边距 |
| resolve | bool | 组件反向排序 |
| crossAxisSpacing | double | 水平子 Widget 之间间距 |
| mainAxisSpacing | double | 垂直子 Widget 之间间距 |
| crossAxisCount | int | 一行的 Widget 数量 |
| childAspectRatio | double | 子 Widget的宽度和高度的比例 |
| children | [ ] | |
| gridDelegate | SliverGridDelegateWithFixedCrossAxisCount(常用)、SliverGridDelegateWithMax | 控制布局主要用在 GridView.builder 里面 |
GridView.count
静态列表
class LayoutDemo extends StatelessWidget {
List<Widget> _getListData() {
var tempList=listData.map((value){
return Container(
child:Column(
children: <Widget>[
Image.network(value['imageUrl']),
SizedBox(height: 12),
Text(
value['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20
),
)
],
),
decoration: BoxDecoration(
border: Border.all(
color:Color.fromRGBO(233, 233,233, 0.9),
width: 1
)
),
// height: 400, //设置高度没有反应
);
});
// ('xxx','xxx')
return tempList.toList();
}
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisSpacing:10.0 , //水平子 Widget 之间间距
mainAxisSpacing: 10.0, //垂直子 Widget 之间间距
padding: EdgeInsets.all(10),
crossAxisCount: 2, //一行的 Widget 数量
// childAspectRatio:0.7, //宽度和高度的比例
children: this._getListData(),
);
}
}
GridView.builder
动态列表
class LayoutDemo extends StatelessWidget {
Widget _getListData (context,index) {
return Container(
child:Column(
children: <Widget>[
Image.network(listData[index]['imageUrl']),
SizedBox(height: 12),
Text(
listData[index]['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20
),
)
],
),
decoration: BoxDecoration(
border: Border.all(
color:Color.fromRGBO(233, 233,233, 0.9),
width: 1
)
),
// height: 400, //设置高度没有反应
);
}
@override
Widget build(BuildContext context) {
return GridView.builder(
//注意
gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing:10.0 , //水平子 Widget 之间间距
mainAxisSpacing: 10.0, //垂直子 Widget 之间间距
crossAxisCount: 2, //一行的 Widget 数量
),
itemCount: listData.length,
itemBuilder:this._getListData,
);
}
}
Padding()组件
有些元素没有padding时,可以外层包裹一层Padding()组件。
有两个参数padding: EdgeInsets.fromLTRB(left, top, right, bottom),、 child: null,必写。
| 属性 | 说明 |
|---|---|
| padding | padding 值, EdgeInsetss 设置填充的值 |
| child | 子组件 |
Widget build(BuildContext context) {
return Padding(
padding:EdgeInsets.fromLTRB(0, 0, 10, 0),
child: GridView.count(
crossAxisCount: 2, //一行数量
childAspectRatio: 1.5,//高宽比例
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Image.network('https://www.itying.com/images/flutter/1.png',fit: BoxFit.cover),
),
Padding(
padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Image.network('https://www.itying.com/images/flutter/2.png',fit: BoxFit.cover),
),
Padding(
padding: EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Image.network('https://www.itying.com/images/flutter/3.png',fit: BoxFit.cover),
)
],
),
);
}
Row 水平布局组件
| 属性 | 说明 |
|---|---|
| mainAxisAlignment | 主轴的排序方式 |
| crossAxisAlignment | 次轴的排序方式 |
| children | 组件子元素 |
return Row( //类型于flex布局
crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(child: Text('Row 组件 1',textAlign: TextAlign.center,)),
Expanded(child: Text('Row 组件 2',textAlign: TextAlign.center,)),
Expanded(child: FittedBox(
fit:BoxFit.contain,
child:const FlutterLogo(),
))
],
);
Column 垂直布局组件
| 属性 | 说明 |
|---|---|
| mainAxisAlignment | 主轴的排序方式 (MainAxisAlignment.spaceEvenly,MainAxisAlignment.center) |
| crossAxisAlignment | 次轴的排序方式 |
| children | 组件子元素 |
return Container( height: 700,
width: 500,
color: Colors.black26,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconContainer(Icons.home,color:Colors.red), IconContainer(Icons.search,color:Colors.blue), IconContainer(Icons.send,color:Colors.orange),
],
),
)
Expanded 类似 Web 中的 Flex 布局
| 属性 | 说明 |
|---|---|
| flex | 元素站整个父 Row /Column 的比例 |
| child | 子元素 |
间距 SizedBox(height:10,width)
2020.03.04
Stack 层叠组件
| 属性 | 说明 |
|---|---|
| alignment | 配置所有子元素的显示位置 alignment: Alignment.center, |
| children | 子组件 |
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
//alignment: Alignment.topCenter,
//alignment: Alignment(0,1),//自定义指定方位(x,y) BottomCenter
children: <Widget>[ //Stack中children的顺序也会导致层叠
Container(
width:200,
height:300,
color:Colors.red,
),
Text(
'我是文本',
style: TextStyle(
fontSize:30,
color: Colors.white
),
),
],
);
}
多个组件需要定位时,可以使用Stack 结合 Align 或者 Positioned 实现定位布局.
Align 组件
Stack 组件中结合 Align 组件可以控制每个子元素的显示位置
| 属性 | 说明 |
|---|---|
| alignment | 配置所有子元素的显示位置 alignment: Alignment.center |
| children | 子组件 |
Positioned组件
Stack 组件中结合 Positioned 组件也可以控制每个子元素的显示位置
| 属性 | 说明 |
|---|---|
| top | 子元素距离顶部的距离 |
| bottom | 子元素距离底部的距离 |
| left | 子元素距离左侧距离 |
| right | 子元素距离右侧距离 |
| child | 子组件 |
Widget build(BuildContext context) {
return Center(
child:Container(
width:300,
height:400,
color:Colors.red,
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: Icon(Icons.home,color:Colors.white),
),
Align(
alignment: Alignment(0.5,0.5),
child: Icon(Icons.search,color:Colors.white),
),
Positioned(
top: 20,
left: 20,
child: Icon(Icons.save,color:Colors.white)
)
],
),
),
);
}
AspectRatio、Card 卡片组件
AspectRatio组件,设置图片的宽高比例
| 属性 | 说明 |
|---|---|
| aspectRatio | 宽高比(20/9) |
| children | 子组件 |
Card卡片组件
| 属性 | 说明 |
|---|---|
| margin | 外边距 EdgeInsets.all(20) |
| children | 子组件 |
| Shape | Card 的阴影效果,默认的阴影效果为圆角的 长方形边。 |
class HomeContent extends StatelessWidget {
List<Widget> _getData(){
var tempList = listData.map((value){
return Card(
margin: EdgeInsets.all(20),
child: Column(
children: <Widget>[
AspectRatio(
aspectRatio: 20/9, //宽高比
child: Image.network(value['imageUrl'],fit: BoxFit.cover,),
),
ListTile(
// leading: Image.network('https://www.itying.com/images/flutter/2.png',fit: BoxFit.cover,),
leading:CircleAvatar(
backgroundImage:NetworkImage(value['imageUrl'])
),
title: Text(value['title']),
subtitle: Text(value['description'],maxLines: 2,overflow:TextOverflow.ellipsis),
),
],
),
);
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
return ListView(
children: this._getData(),
);
}
}
RaisedButton 定义一个按钮
class MyButton extends StatelessWidget {
final String text;
const MyButton(this.text,{Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return RaisedButton(
child: Text(this.text),
textColor:Theme.of(context).accentColor,
color:Colors.red,
onPressed: (){}
);
}
}
wrap组件实现流布局,自动换行
| 属性 | 说明 | 应用 |
|---|---|---|
| direction | 主轴的方向,默认水平 | direction:Axis.vertical/Axis.horizontal |
| alignment | 主轴的对其方式 | alignment:WrapAlignment.start |
| spacing | 主轴方向上的间距 | spacing:10 |
| runAlignment | run 的对齐方式。run 可以理解为新的行或者 列,如果是水平方向布局的话,run 可以理解 为新的一行 | runAlignment: WrapAlignment.spaceAround |
| runSpacing | run 的间距 | runSpacing:10 |
| textDirection | 文本方向 | |
| verticalDirection | 定义了 children 摆放顺序,默认是 down,见 Flex 相关属性介绍。 |
StateLessWidget 无状态组件
StatefulWidget 有状态组件
- StatelessWidget 是无状态组件,状态不可变的 widget;
- StatefulWidget 是有状态组件,持有的状态可能在 widget 生命周期改变。通俗的讲:如果我 们想改变页面中的数据的话这个时候就需要用到 StatefulWidget
页面上绑定数据、改变页面数据
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int count = 1;
List list = new List();
get textDirection => null;
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
RaisedButton(
child: Text('按钮'),
onPressed: (){
setState(() {
this.list.add('新增数据${this.count++}');
});
}
),
RaisedButton(
child: Text('清空'),
onPressed: (){
setState(() {
this.list.clear();
this.count = 1;
});
}
),
SizedBox(
height:20,
),
Column(
children:this.list.map((value){
return Padding(padding: EdgeInsets.all(10),
child:Text(value,style: TextStyle(fontSize: 20))
);
}).toList(),
),
],
);
}
}
2020.03.05
BottomNavigationBar 组件
BottomNavigationBar 是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar 是 Scaffold 组件的参数。
| 属性 | 说明 |
|---|---|
| items | List<BottomNavigationBarItem> 底 部 导 航 条按钮集合 |
| iconSize | icon大小 |
| currentIndex | 默认选中第几个 |
| fixedColor | 选中的颜色 |
| type | BottomNavigationBarType.fixed BottomNavigationBarType.shifting |
| onTap | 选中变化回调函数 |
底部导航就会涉及到页面切换,所以需要代码抽离,如下图:在Tabs.dart中设置。
import 'package:flutter/material.dart';
import './tabs/HomePage.dart';
import './tabs/CategoryPage.dart';
import './tabs/SettingsPage.dart';
class HomeTabs extends StatefulWidget {
HomeTabs({Key key}) : super(key: key);
@override
_HomeTabsState createState() => _HomeTabsState();
}
class _HomeTabsState extends State<HomeTabs> {
int _currentIndex = 0;
List _pageList=[
HomePage(),
CategoryPage(),
SettingsPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fultter demo')),
body: this._pageList[this._currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: this._currentIndex, //配置的索引值选中
iconSize: 30.0, //icon大小
fixedColor: Colors.red, //选中的颜色
type: BottomNavigationBarType.fixed, //shifting默认 要设置固定fixed
onTap:(int index){ //此处需要改变state值,需要放到有状态组件中
setState(() {
this._currentIndex = index;
});
},
items: [ //底 部 导 航 条按钮集合
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('设置')
)
]
),
);
}
}
2020.03.06
路由
普通路由Navigator.push 和 Navigator.pop
- 跳转
- 跳转传值
Center(
child: RaisedButton(
child: Text('跳转到查询页面'),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>SearchPage(title:'传值查询') //传值
)
);
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary
),
)
命名路由Navigator.pushNamed(context, '/form');
1. 在MaterialApp中配置routes,跳转Navigator.pushNamed(context, '/form');
return MaterialApp(
home: HomeTabs(),
routes: {
'/search':(context)=>SearchPage(),
'/form':(context)=>FormPage(),
},
theme: ThemeData(primarySwatch: Colors.yellow),
);
RaisedButton(
child: Text('命名路由跳转/传值'),
onPressed: (){
Navigator.pushNamed(context, '/form');
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary
),
2. 抽离路由配置并传值
(1) 新建Routes.dart文件
import 'package:flutter/material.dart';
import '../pages/Search.dart';
import '../pages/Form.dart';
//配置路由
final routes={
'/search':(context)=>SearchPage(),
'/form':(context,{arguments})=>FormPage(arguments:arguments),
};
//固定写法
var onGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name;
final Function pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
}else{
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context));
return route;
}
}
};
(2) main.dart引入
import 'package:flutter/material.dart';
import './pages/Tabs.dart';
import './routes/Routes.dart';
void main() => runApp(MyApp());
//自定义组件
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeTabs(),
initialRoute: '/', //初始化的时候加载的路由
onGenerateRoute:onGenerateRoute,//监听路由
theme: ThemeData(primarySwatch: Colors.yellow),
);
}
}
(3) Home.dart引用传值
RaisedButton(
child: Text('命名路由跳转/传值'),
onPressed: (){
Navigator.pushNamed(context, '/form',arguments:{
'id':1212
});
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary
),
(4) From.dart接收传值
StatelessWidget无状态组件中接收值:
class FormPage extends StatelessWidget {
final arguments;
FormPage({this.arguments});//构造函数
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('表单页面')),
body:Container(
child: Text("表单页面${arguments != null ? arguments['id'] : '0'}"),
)
);
}
}
StatefulWidget有状态组件中接收值:
class FormPage extends StatefulWidget {
final arguments;
FormPage({this.arguments});//构造函数
@override
_FormPageState createState() => _FormPageState(this.arguments);
}
class _FormPageState extends State<FormPage> {
var arguments;
_FormPageState(arguments){
this.arguments = arguments;
}//构造函数
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('表单页面')),
body:Container(
child: Text("表单页面${arguments != null ? arguments['id'] : '0'}"),
)
);
}
}
返回到上一级页面
Navigator.of(context).pop();
下一步
Navigator.pushNamed(context, '/registerSecond');
路由替换
Navigator.of(context).pushReplacementNamed('/registerSecond');
返回根路由
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new Tabs()),
(route) => route == null
);
指定返回哪个根路由:
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new Tabs(index:1)),
(route) => route == null
);
Tabs.dart接收index参数:
import 'package:flutter/material.dart';
import './tabs/HomePage.dart';
import './tabs/CategoryPage.dart';
import './tabs/SettingsPage.dart';
class Tabs extends StatefulWidget {
final index ;
Tabs({this.index = 0 });
@override
_TabsState createState() => _TabsState(this.index);
}
class _TabsState extends State<Tabs> {
int _currentIndex;
List _pageList=[
HomePage(),
CategoryPage(),
SettingsPage()
];
_TabsState(index){
this._currentIndex = index;
}
List _titleList=[
'首页','分类','设置'
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this._titleList[this._currentIndex])),
body: this._pageList[this._currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: this._currentIndex, //配置的索引值选中
iconSize: 30.0, //icon大小
fixedColor: Colors.red, //选中的颜色
type: BottomNavigationBarType.fixed, //shifting默认 要设置固定fixed
onTap:(int index){ //此处需要改变state值,需要放到有状态组件中
setState(() {
this._currentIndex = index;
});
},
items: [ //底 部 导 航 条按钮集合
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('设置')
)
]
),
);
}
}
AppBar组件
自定义顶部样式
| 属性 | 描述 |
|---|---|
| leading | 在标题前面显示的一个控件,在首页通常显示应用 的 logo;在其他界面通常显示为返回按钮 |
| title | 标题,通常显示为当前界面的标题文字,可以放组 件 |
| actions | 通常使用 IconButton 来表示,可以放按钮组 |
| bottom | 通常放 tabBar,标题下面显示一个 Tab 导航栏 |
| backgroundColor | 导航背景颜色 |
| iconTheme | 图标样式 |
| textTheme | 文字样式 |
| centerTitle | 标题是否居中显示 |
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title: Text('AppBarPage',style:TextStyle(color:Colors.white)),
backgroundColor: Colors.blue,
// leading: Icon(Icons.menu,color: Colors.white),
leading: IconButton( //在首页通常显示应用 的 logo;在其他界面通常显示为返回按钮
icon: Icon(Icons.menu,color: Colors.white),
onPressed: (){
print('menu');
}
),
actions: <Widget>[//按钮组
IconButton(
icon: Icon(Icons.search,color: Colors.white),
onPressed: (){
print('search');
}
),
IconButton(
icon: Icon(Icons.settings,color: Colors.white),
onPressed: (){
print('settings');
}
),
],
centerTitle:true,//始终居中显示
),
body: Container(
child: Text('appbar'),
),
);
}
自定义 TabBar 实现顶部 Tab 切换
TabBar 常见属性:
| 属性 | 描述 |
|---|---|
| tabs | 显示的标签内容,一般使用 Tab 对象,也可以是其他 的 Widget |
| controller | TabController 对象 |
| isScrollable | 是否可滚动 |
| indicatorColor | 指示器颜色 |
| indicatorWeight | 指示器高度 |
| indicatorPadding | 底部指示器的 Padding |
| indicator | 指示器 decoration,例如边框等 |
| indicatorSize | 指示器大小计算方式,TabBarIndicatorSize.label 跟文 字等宽,TabBarIndicatorSize.tab 跟每个 tab 等宽 |
| labelColor | 选中 label 颜色 |
| labelStyle | 选中 label 的 Style |
| labelPadding | 每个 label 的 padding 值 |
| unselectedLabelColor | 未选中 label 颜色 |
| unselectedLabelStyle | 未选中 label 的 Style |
class _AppBarPageState extends State<AppBarPage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2, //必须配置
child: Scaffold(
appBar:AppBar(
title: Text('AppBarPage',style:TextStyle(color:Colors.white)),
backgroundColor: Colors.blue,
// leading: Icon(Icons.menu,color: Colors.white),
leading: IconButton( //在首页通常显示应用 的 logo;在其他界面通常显示为返回按钮
icon: Icon(Icons.menu,color: Colors.white),
onPressed: (){
print('menu');
}
),
// actions: <Widget>[//按钮组
// IconButton(
// icon: Icon(Icons.search,color: Colors.white),
// onPressed: (){
// print('search');
// }
// ),
// IconButton(
// icon: Icon(Icons.settings,color: Colors.white),
// onPressed: (){
// print('settings');
// }
// ),
// ],
centerTitle:true,//始终居中显示
bottom: TabBar(
indicatorColor:Colors.orange,
labelColor:Colors.white,
indicatorSize:TabBarIndicatorSize.label,
tabs: <Widget>[
Tab(text:'热门'),
Tab(text:'推荐'),
],
),
),
body: TabBarView(children: <Widget>[
ListView(children: <Widget>[
ListTile(
title:Text('第一个tab')
),
ListTile(
title:Text('第一个tab')
),
ListTile(
title:Text('第一个tab')
),
],
),
ListView(children: <Widget>[
ListTile(
title:Text('第二个tab')
),
ListTile(
title:Text('第二个tab')
),
ListTile(
title:Text('第二个tab')
),
],
),
]
),
),
);
}
}
TabBar 放在导航最顶部
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child:Scaffold(
appBar:AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex:1,
child: TabBar(//因为 ategoryPage外层也有一层Scaffold,所以用title取代bottom
indicatorColor:Colors.red,
// labelColor:Colors.white,
indicatorSize:TabBarIndicatorSize.label,
tabs: <Widget>[
Tab(text:'热门'),
Tab(text:'推荐'),
Tab(text:'热销'),
Tab(text:'优惠'),
],
)
)
]),
),
body: TabBarView(children: <Widget>[])
)
}
TabController实现Tabs切换
AppBar 中自定义 TabBar 实 现 Tabs 的另一种方法。
import 'package:flutter/material.dart';
class TabBarControllerPage extends StatefulWidget {
TabBarControllerPage({Key key}) : super(key: key);
@override
_TabBarControllerPageState createState() => _TabBarControllerPageState();
}
//1 添加 with SingleTickerProviderStateMixin
class _TabBarControllerPageState extends State<TabBarControllerPage> with SingleTickerProviderStateMixin{
//2 定义_tabController
TabController _tabController;
//3 初始化
@override
void initState(){ //生命周期函数
super.initState();
_tabController = new TabController(vsync: this,length: 2);
_tabController.addListener((){//6 监听tab切换事件
print(_tabController.index);
});
}
@override
void dispose(){ //销毁的生命周期
super.dispose();
_tabController.dispose(); //销毁
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title:Text('TabBarController'),
bottom: TabBar(
controller: this._tabController,//4 在TabBar中配置controller
tabs: <Widget>[
Tab(text:'热销'),
Tab(text:'推荐')
],
),
),
body:TabBarView(
controller: this._tabController,//5 在TabBarView中配置controller
children: <Widget>[
Center(child:Text('热销')),
Center(child:Text('推荐')),
],
)
);
}
}
Drawer抽屉组件
Drawer 侧边栏
return Scaffold(
appBar: AppBar(
title: Text("Flutter App"), ),
drawer: Drawer(
child: Text('左侧边栏'),
),
endDrawer: Drawer(
child: Text('右侧侧边栏'),
),
);
DrawerHeader头部
| 属性 | 描述 |
|---|---|
| decoration | 设置顶部背景颜色 |
| child | 配置子元素 |
| padding | 内边距 |
| margin | 外边距 |
UserAccountsDrawerHeader用户信息头部
| 属性 | 描述 |
|---|---|
| decoration | 设置顶部背景颜色 |
| accountName | 账户名称 |
| accountEmail | 账户邮箱 |
| currentAccountPicture | 用户头像 |
| otherAccountsPictures | 用来设置当前账户其他账户头像 |
| margin | 外边距 |
侧边栏路由跳转
onTap: (){
Navigator.of(context).pop();
Navigator.pushNamed(context, '/search');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this._titleList[this._currentIndex])),
body: this._pageList[this._currentIndex],
drawer: Drawer(//抽屉Text('左侧抽屉栏')
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
// child: DrawerHeader(
// child: Text('你好,flutter'),
// decoration: BoxDecoration(
// color: Colors.yellow,
// image: DecorationImage(
// image: NetworkImage(
// 'https://www.itying.com/images/flutter/2.png'),
// fit: BoxFit.cover)),
// ),
child: UserAccountsDrawerHeader(
accountName: Text('苗苗'),
accountEmail: Text('miaomiao@123.com'),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage('https://www.itying.com/images/flutter/4.png'),
),
decoration: BoxDecoration( //背景图片
image: DecorationImage(
image: NetworkImage( 'https://www.itying.com/images/flutter/2.png'), fit: BoxFit.cover)
),
),
)
],
),
ListTile(
leading: CircleAvatar(child: Icon(Icons.home)),
title: Text('我的空间'),
onTap: (){
Navigator.of(context).pop();//隐藏侧边栏
Navigator.pushNamed(context, '/login');
},
),
Divider(),
ListTile(
leading: CircleAvatar(child: Icon(Icons.phone)),
title: Text('用户中心')),
Divider(),
ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('设置中心'))
],
),
),
endDrawer: Drawer(child: Text('右侧抽屉栏')),
);
}
2020.03.09
按钮组件
常见的按钮组件有:RaisedButton、FlatButton、IconButton、OutlineButton、ButtonBar、FloatingActionButton 等。
RaisedButton:凸起的按钮,其实就是 Material Design 风格的 ButtonFlatButton:扁平化的按钮OutlineButton:线框按钮IconButton:图标按钮ButtonBar:按钮组 FloatingActionButton:浮动按钮
| 属性名称 | 值类型 | 属性值 |
|---|---|---|
| onPressed | VoidCallback ,一般接收一个 方法 | 必填参数,按下按钮时触发的回调,接收一个 方法,传 null 表示按钮禁用,会显示禁用相关 样式 |
| child | Widget | 文本控件 |
| textColor | Color | 文本颜色 |
| color | Color | 按钮的颜色 |
| disabledColor | Color | 按钮禁用时的颜色 |
| disabledTextColor | Color | 按钮禁用时的文本颜色 |
| splashColor | Color | 点击按钮时水波纹的颜色 |
| highlightColor | Color | 点击(长按)按钮后按钮的颜色 |
| elevation | double | 阴影的范围,值越大阴影范围越大 |
| padding | 内边距 | |
| shape | 设置按钮的形状RoundedRectangleBorde(borderRadius:BorderRadius.circular(10),) |
class _ButtonPageState extends State<ButtonPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('按钮组件')),
body:Container(
// padding: EdgeInsets.all(20),
child:Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container( //设置RaisedButton的高宽可以放置在Container中设置
width: 200,
height: 60,
child: RaisedButton(
child: Text('普通按钮'),
onPressed: (){
print('按钮');
},
textColor:Colors.orangeAccent, //文本颜色
color:Colors.white, //按钮的颜色
// padding: EdgeInsets.fromLTRB(50, 10, 50, 10),//内边距
shape: RoundedRectangleBorder( //设置按钮的形状
borderRadius:BorderRadius.circular(20)
),
// shape: CircleBorder( //圆形
// // side:BorderSide(color:Colors.red)
// ),
splashColor: Colors.blue, //点击按钮时水波纹的颜色
highlightColor:Colors.red, //点击(长按)按钮后按钮的颜色
elevation: 3.0,
// disabledColor:Colors.grey, //按钮禁用时的颜色
// disabledTextColor:Colors.black, //按钮禁用时的文本颜色
)
),
]),
SizedBox(height: 50,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //
child:
Container( //设置RaisedButton的高宽可以放置在Container中设置
height: 60,
margin: EdgeInsets.all(20),
child: RaisedButton(
child: Text('全屏按钮--Expanded'),
color: Colors.blue,
textColor: Colors.white,
onPressed: (){
print('全屏按钮');
},
)
),
)
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //
child:
Container( //设置RaisedButton的高宽可以放置在Container中设置
height: 60,
margin: EdgeInsets.all(20),
child: FlatButton(
child: Text('扁平化按钮,无阴影'),
color: Colors.blue,
textColor: Colors.white,
onPressed: (){
},
)
),
)
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //
child:
Container(
child:ButtonBar(children: <Widget>[
Icon(Icons.home),
Icon(Icons.people),
Icon(Icons.wallpaper),
Icon(Icons.query_builder),
],
alignment: MainAxisAlignment.spaceAround,
mainAxisSize:MainAxisSize.max,
buttonPadding: EdgeInsets.all(30),
)
),
)
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //
child:
Container( //设置RaisedButton的高宽可以放置在Container中设置
height: 60,
margin: EdgeInsets.all(20),
child: OutlineButton(
child: Text('线框按钮'),
onPressed: (){
},
)
),
)
]),
],)
),
//浮动按钮
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add,color: Colors.yellow,size: 44,),
onPressed: (){},
backgroundColor: Colors.red,
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
FloatingActionButton浮动按钮组件
| 属性名称 | 属性值 |
|---|---|
| child | 子视图,一般为 Icon,不推荐使用文字 |
| tooltip | FAB 被长按时显示,也是无障碍功能 |
| backgroundColor | 背景颜色 |
| elevation | 未点击的时候的阴影 |
| hignlightElevation | 点击时阴影值,默认 12.0 |
| onPressed | 点击事件回调 |
| shape | 可以定义 FAB 的形状等 |
| mini | 是否是 mini 类型默认 false |
FloatingActionButton简称FAB ,可以实现浮动按钮,也可以实现类似闲鱼app的地步凸
起导航
- 浮动按钮:
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - 实现闲鱼 app 底部凸起按钮
在Scaffold()里实现
//floatingActionButton的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
//FloatingActionButton 实现闲鱼 app 底部凸起按钮
floatingActionButton:Container(
width: 60,
height: 60,
// padding: EdgeInsets.all(10),
margin: EdgeInsets.only(top:15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
color: Colors.red,
),
// color: Colors.red,
child: FloatingActionButton(
child: Icon(Icons.add,size: 44,),
backgroundColor: this._currentIndex == 1 ? Colors.red :Colors.blue,//背景颜色
elevation: 0.0, //未点击的时候的阴影
highlightElevation: 12.0 ,//点击时阴影值,默认 12.0
onPressed: (){//点击事件回调
print('FloatingActionButton');
setState(() {
this._currentIndex = 1;
});
},
// tooltip:'9900' ,//FAB 被长按时显示,也是无障碍功能
// shape:RoundedRectangleBorder( //可以定义 FAB 的形状等
// borderRadius: BorderRadius.circular(10), //自定义
// ),
// shape: CircleBorder( //圆形
// side: BorderSide(
// color: Colors.white,
// )
// ),
),
tooltip长按效果:
宽度自适应:width:double.infinity
表单
TextField 单行文本框,TextField 多行文本框
| 属性 | 描述 |
|---|---|
| maxLines | 设置此参数可以把文本框改为多行文本框 |
| onChanged | 文本框改变的时候触发的事件 |
| decoration | hintText:类似 html 中的 placeholder; |
| - | border:配置文本框边框 OutlineInputBorder 配合使用 |
| - | labelText :lable 的名称 |
| - | labelStyle:配置 lable 的样式 |
| obscureText | 把文本框框改为密码框 |
| controller | controller 结合 TextEditingController()可以配置表单默认显示的内容 |
CheckBox、CheckboxListTile多选框组件
Radio、RadioListTile单选框组件
Switch、SwitchListTile
Checkbox ,Radio,Switch常见属性:
| 属性 | 描述 |
|---|---|
| value | Checkbox(true 或者 false);Radio(0/1);Switch(true 或者 false) |
| onChanged | 改变的时候触发的事件 |
| activeColor | 选中的颜色、背景颜色 |
| checkColor | 选中的颜色、Checkbox 里面对号的颜色 |
CheckboxListTile 常见属性:
| 属性 | 描述 |
|---|---|
| value | true 或者 false |
| onChanged | 改变的时候触发的事件 |
| activeColor | 选中的颜色、背景颜色 |
| title | 标题 |
| subtitle | 二级标题 |
| secondary | 配置图标或者图片 |
| selected | 选中的时候文字颜色是否跟着改变 |
学习暂且告一段落~