1. Container 容器
默认情况下是包裹内容的,如果需要宽度占满屏幕宽度,需要设定 width 为double.infinity 。
Container(
// 按钮控件
width: double.infinity,
height: 48,
margin: EdgeInsets.all(10),
child: ...,
),
2. ElevationButton
可以设置阴影的 Button
ElevatedButton( //有水波纹效果
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.green),
elevation: MaterialStateProperty.all<double>(10),
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
new CommonButtonPage()));
},
child: Text('按钮控件')
),
3. MaterialButton
MaterialButton(
onPressed: () {},
child: Text('MaterialButton占满一行'),
minWidth: double.infinity,
height: 50.0,
color: Colors.blue,
textColor: Colors.white,
),
4. SizedBox
SizedBox(
width: double.infinity,
height: 50.0,
child: ElevatedButton(
onPressed: () {},
child: Text('SizedBox占满一行'),
),
),
5. InkWell
InkWell(
overlayColor: MaterialStateColor.resolveWith((states) => Colors.green),
onTap: () {},
child: new Container(
alignment: Alignment.center, // alignment将内容布局到中间
padding: new EdgeInsets.all(10),
child: new Text(
'RippleView',
style: TextStyle(backgroundColor: Colors.amber),
),
),
),
6. Image 加载网络图片
//这块要求小写驼峰命名方式来命名方法名称
_createNetworkImage() {
var url = 'http://222.186.12.239:20011/mm8/tupai/20160122/k2xvpyl3wpl.jpg';
return Image.network(
url,
//width: double.infinity,
//fit: BoxFit.fitWidth, // 以宽度来适配
//fit: BoxFit.scaleDown, // 以缩小来适配
fit: BoxFit.cover, // 以整个图片贴在控件上来适配
width: 300,
height: 100,
);
}
7. Image 加载Asset 图片
// 加载本地图片
// 1. 在本地根目录下创建 images 目录,同时添加图片 lovely_girl.jpg
// 2. 在pubspec.yaml 文件中的
// flutter:
// assets:
// - images/lovely_girl.jpg
//或者加载整个images目录下所有图片 - images/
_createLocalImage() {
var url = 'images/lovely_girl.jpg';
return Image.asset(
url,
width: 300,
height: 100,
);
}
// 用AssetImage来加载图片
_createAssetImage() {
return Image(
image: AssetImage('images/lovely_girl.jpg'),
width: 300,
height: 100,
);
}
8. Image 加载本地磁盘图片
_createFutureImage() {
return FutureBuilder(
future: _getLocalFile('lovely_girl.jpg'),
builder: (BuildContext context, AsyncSnapshot<File> snapShot) {
return snapShot.data != null
? Image.file(snapShot.data)
: Container();
});
}
Future<File> _getLocalFile(String fileName) async {
// String dir = (await getExternalStorageDirectory()).path;
// String dir = (await getTemporaryDirectory()).path; // /data/user/0/com.luckyboy.flutterapp/cache
String dir = (await getApplicationDocumentsDirectory())
.path; // /data/user/0/com.luckyboy.flutterapp/app_flutter/lovely_girl.jpg
print('_getLocalFile $dir');
File f = new File('$dir/$fileName');
return f;
}
9. Image 淡入淡出图片
_createMemoryNetworkImage() {
const url =
'http://222.186.12.239:20011/mm8/tupai/20160122/k2xvpyl3wpl.jpg';
return FadeInImage.memoryNetwork(
width: 300,
height: 300,
placeholder: kTransparentImage,
image: url,
fadeInDuration: Duration(seconds: 2),
fadeOutDuration: Duration(seconds: 2),
);
}
10. GridView.Builder 网格布局
Container(
padding: EdgeInsets.all(8),
child: GridView.builder(
shrinkWrap: true,
itemCount: listItems.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 4.0, // 水平子Widget之间的间距
mainAxisSpacing: 4.0, // 垂直子Widget之间的间距
childAspectRatio: 3, // 子Widget宽高比例
crossAxisCount: 3, // 一行的Widget的数量
),
itemBuilder: (context, index) {
return _buildItem(listItems[index]);
}),
),
_buildItem(city) {
return Container(
height: 40,
alignment: Alignment.center,
decoration: BoxDecoration(color: Colors.blue),
child: Text(city, style: TextStyle(color: Colors.white, fontSize: 14)),
);
}
11. 水平滚动布局
Container(
height: 60,
child: ListView.builder(
scrollDirection: Axis.horizontal, //滚动方向
itemBuilder: (context, index) {
return _buildItem(listItems[index]);
},
itemCount: listItems.length,
),
),
12. 滑动删除列表
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Dismissible(
key: Key(item),
child: ListTile(
title: Text('$item'),
),
onDismissed: (direction) {
items.removeAt(index);
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text('$item dismissed')));
setState(() {});
},
background: Container(
color: Colors.red,
),
);
}),
13. 展开子列表
class CommonListExpansionPage extends StatelessWidget {
static const CITY_NAMES = {
'北京': ['东城区', '西城区', '朝阳区', '海淀区', '丰台区', '昌平区', '石景山区', '顺义区'],
'上海': ['黄浦区', '徐汇区', '长宁区', '静安区', '普陀区', '闸北区', '虹口区'],
'广州': ['越秀', '海珠', '荔湾', '天河', '白云', '黄埔', '南沙', '番禺'],
};
@override
Widget build(BuildContext context) {
final title = '列表展开与收起';
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(children: _listExpansionWidgets()),
);
}
List<Widget> _listExpansionWidgets() {
List<Widget> widgets = [];
CITY_NAMES.keys.forEach((city) {
widgets.add(_buildItem(city, CITY_NAMES[city]));
});
return widgets;
}
Widget _buildItem(String city, List<String> subCitys) {
return ExpansionTile(
title: Text(
city,
style: TextStyle(color: Colors.black54, fontSize: 14),
),
children: subCitys.map((subCity) => _buildSub(subCity)).toList(),
onExpansionChanged: (bool expand) {
print('$expand');
},
);
}
Widget _buildSub(String subCity) {
return FractionallySizedBox(
widthFactor: 1,
child: Container(
height: 50,
margin: EdgeInsets.only(left: 16, bottom: 0),
//decoration: BoxDecoration(color: Colors.grey),
child: Text(subCity),
),
);
}
}
14. 列表加载更多
class CommonListLoadMorePage extends StatefulWidget {
@override
_CommonListLoadMorePageState createState() => _CommonListLoadMorePageState();
}
class _CommonListLoadMorePageState extends State<CommonListLoadMorePage> {
var listItems = [
'北京',
'上海',
'广州',
'深圳',
'杭州',
'苏州',
'天津',
'武汉',
'成都',
'西安',
'重庆',
'昆明',
'兰州',
'乌鲁木齐',
'太原'
];
static const PAGE_SIZE = 15;
bool disposed = false;
ScrollController _scrollController = new ScrollController();
final GlobalKey<RefreshIndicatorState> refreshKey = new GlobalKey();
@override
void initState() {
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
// 加载到最底部位置 准备加载更多数据
_loadMoreData();
}
});
Future.delayed(Duration(seconds: 0), () {
refreshKey.currentState.show();
});
super.initState();
}
@override
Widget build(BuildContext context) {
final title = '列表加载更多控件';
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: RefreshIndicator(
key: refreshKey,
onRefresh: _handleRefresh,
child: ListView.builder(
// 保持ListView在任何情况下都能滚动,解决在RefreshIndicator的兼容性问题
physics: const AlwaysScrollableScrollPhysics(),
controller: _scrollController,
itemCount: listItems.length >= PAGE_SIZE
? listItems.length + 1
: listItems.length,
itemBuilder: (context, index) {
if (index == listItems.length) {
return Container(
width: double.infinity,
height: 50,
margin: EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Align(
child: CircularProgressIndicator(),
),
);
}
return _buildItem(listItems[index]);
}),
),
);
}
_buildItem(city) {
return Container(
width: double.infinity,
height: 50,
margin: EdgeInsets.fromLTRB(10, 5, 10, 5),
child: ElevatedButton(
onPressed: () {},
child: Text(city ?? ''),
),
);
}
Future<Null> _handleRefresh() async {
await Future.delayed(Duration(seconds: 2));
//1. 创建一个指定数量的List
List<String> target = []..length = listItems.length;
//2. 将旧数据拷贝到新的List中
List.copyRange(target, 0, listItems);
//3. 将新的List中元素进行修改
List<String> targetNnew = target.map((e) => e + "A").toList();
listItems.clear();
//4. 添加新的数据源到列表中
listItems.addAll(targetNnew);
if (disposed) {
return;
}
setState(() {});
return null;
}
Future<Null> _loadMoreData() async {
await Future.delayed(Duration(seconds: 1));
listItems.add("更多城市");
if (disposed) {
return;
}
setState(() {});
return null;
}
@override
void dispose() {
disposed = true;
_scrollController.dispose();
super.dispose();
}
}
15. 具有分割线的列表
Container(
child: ListView.separated(
itemBuilder: (context, index) {
return _buildItem(listItems[index]);
},
separatorBuilder: (context, index) {
return Container(
constraints: BoxConstraints.tightFor(height: 1),
color: Colors.black45,
);
},
itemCount: listItems.length),
),
16. Align
import 'package:flutter/material.dart';
import 'dart:math' as math;
class AlignPage extends StatefulWidget {
const AlignPage({Key key}) : super(key: key);
@override
_AlignPageState createState() => _AlignPageState();
}
class _AlignPageState extends State<AlignPage> with SingleTickerProviderStateMixin {
int size = 20;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AlignPage'),),
body: Container(
alignment: Alignment(0, 0),
child: Container(
width: MediaQuery.of(context).size.width/2,
height: MediaQuery.of(context).size.width/2,
child: Stack(
children: List.generate(size, (index){
return getAlign(index.toDouble()/(size/2));
}),
),
),
),
);
}
Widget getAlign(double x) {
return Align( //Center 就是默认的Align
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(10))
),
),
//注意坐标范围:x,y都在 在【-1, 1】之间,以向右为x轴正方向,向下为y轴正方向
// 表示该child在父布局的坐标位置 alignment
alignment: Alignment(math.cos(x*math.pi), math.sin(x*math.pi)),
//alignment: Alignment(x, y),
);
}
}
效果图: