1.主题色主题色设置
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',//针对Android 里面可用
theme: ThemeData(
primarySwatch: Colors.yellow,//主题色设置,深色:则时间和电池颜色为白色;浅色:则时间和电池颜色为黑色
highlightColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
splashColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
),
home: RootPage(),
);
}
}
2.ListView去掉iPhoneX刘海
MediaQuery.removePadding
-> removeTop: true
Container(
color: Color.fromRGBO(220, 220, 220, 1.0),
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView(
children: <Widget>[
Container(
color: Colors.white,
height: 200,
),
SizedBox(height: 10,),
DiscoverCell(imageName: 'images/微信支付1.png',title: '支付',),
],
),
),
),
3.Image设置圆角
Row(
children: <Widget>[
Container(
width: 70,
height: 70,
// child: Image(image: AssetImage('images/Steven.png'),),//写在此处设置圆角无效
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10.0),
image: DecorationImage(image:AssetImage('images/Steven.png'),
fit: BoxFit.cover)//设置图片的填充模式
),
),//头像
Container(),//右边部分
],
)
设置圆形图片
CircleAvatar(
backgroundImage: new AssetImage('images/1.jpeg'),
radius: 100.0,
)
4.设备的宽高获取
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
5.文字居中方向设置
//Container属性
alignment: Alignment.centerLeft,
6.网络图片和本地图片的加载
Container(
width: 34,
height: 34,
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(
image: imageUrl != null
? NetworkImage(imageUrl) //网络图片
: AssetImage(imageAssets),//本地图片
)),
)
7.链式编程-添加数据
@override
//数据、对象创建
void initState() {
super.initState();
//链式编程,调用两次addAllData并返回数组到 _listDatas
_listDatas..addAll(datas)..addAll(datas);
//数据排序
_listDatas.sort((Friends a, Friends b){
return a.indexLetter.compareTo(b.indexLetter);
});
//print('_listDatas:$_listDatas');
}
8.相除取整
~/
onVerticalDragUpdate: (DragUpdateDetails details){
print(details.globalPosition.dy);//相对于整个屏幕的值
RenderBox box = context.findRenderObject();
//计算当前位置 坐标转换, 算出y值
double y = box.globalToLocal(details.globalPosition).dy;
//y值除以每个item的高度就是当前的索引
//每一个item的高度
var itemH = ScreenHeight(context)/2/INDEX_WORDS.length;
int index = y ~/ itemH;//相除取整
print(box.globalToLocal(details.globalPosition));
},
9.数组越界处理
//使用clamp
//取值范围0~INDEX_WORDS.length-1 添加安全判断
int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);
10.定义回调函数和调用
//定义回调函数
final void Function(String str) indexBarCallBack;
//构造方法
const IndexBar({Key key, this.indexBarCallBack}) : super(key: key);
//调用该callBack
//监听所在位置:计算当前位置
onVerticalDragUpdate: (DragUpdateDetails details){
widget.indexBarCallBack(getIndex(context, details.globalPosition));
},
//外部使用
IndexBar(
indexBarCallBack: (String str){
print("收到了:$str");
},
),
11.PopupMenuButton 使用
Container(
margin: EdgeInsets.only(right: 10),
child: PopupMenuButton(
offset: Offset(0, 60.0),
child: Image(image: AssetImage('images/圆加.png'),width: 25,),
itemBuilder: _buildPopupMenuItem,
),
)
//创建Item的方法!
PopupMenuItem<String> _buildItem(String imgAss, String title) {
return PopupMenuItem(
child: Row(
children: <Widget>[
Image(
image: AssetImage(imgAss),
width: 20,
),
Container(
width: 20,
),
Text(
title,
style: TextStyle(color: Colors.white),
),
],
),
);
}
//回调方法
List<PopupMenuItem<String>> _buildPopupMenuItem(BuildContext context) {
return <PopupMenuItem<String>>[
_buildItem('images/发起群聊.png', '发起群聊'),
_buildItem('images/添加朋友.png', '添加朋友'),
_buildItem('images/扫一扫1.png', '扫一扫'),
_buildItem('images/收付款.png', '收付款'),
];
}
设置popup背景颜色
//MaterialApp -> theme -> cardColor
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',//针对Android 里面可用
theme: ThemeData(
primarySwatch: Colors.yellow,//主题色设置,深色:则时间和电池颜色为白色;浅色:则时间和电池颜色为黑色
highlightColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
splashColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
cardColor: Color.fromRGBO(1, 1, 1, 0.65),//设置popup背景颜色
),
home: RootPage(),
)
效果图:
12.滑动ListView让键盘消失
FocusScope.of(context).requestFocus(FocusNode());
//监听ListView的滑动事件,让键盘消失
Expanded(
flex: 1, //占据剩余空间
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: NotificationListener(
onNotification: (ScrollNotification note){
FocusScope.of(context).requestFocus(FocusNode());
},//滑动让键盘消失
child: ListView.builder(
itemCount: _models.length,
itemBuilder: _itemForRow,
),
),
),
)
13.Containter 设置部分圆角和阴影效果
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10, bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
// 设置阴影 要在裁剪之外添加一个Containter里面处理,否则无效
boxShadow: [
BoxShadow(
color: Color(0xff333333).withOpacity(0.05),
offset: Offset(0, 1.0),
blurRadius: 5),
],
),
child: new ClipRRect(
// 设置局部圆角
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
child: Container(
height: ScreenUtil().setHeight(147),
child: Container(
margin: EdgeInsets.only(left: 40, right: 40),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: navigatorList.map((item) {
return _navigatorItem(context, item);
}).toList(),
),
),
),
),
);
}
14. Scaffold 的 appBar 去掉底部阴影
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
title: Text(
"发现",
style: TextStyle(
color: Color(0xff333333), fontSize: ScreenUtil().setSp(34)),
),
bottomOpacity: 0,
elevation: 0, // 去掉底部阴影
),
);
其他
@override
Widget build(BuildContext context) {
return Scaffold(
//头部元素 比如:左侧返回按钮 中间标题 右侧菜单
appBar: AppBar(
title: Text('Scaffold脚手架组件示例'),
),
//视图内容部分 通常作为应用页面的主显示区域
body: Center(
child: Text('Scaffold'),
),
//底部导航栏
bottomNavigationBar: BottomAppBar(
child: Container(height: 50.0,),
),
//添加FAB按钮
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: '增加',
child: Icon(Icons.add),
),
//FAB按钮居中展示
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
15.Image图片设置宽度自适应
Container(
margin: EdgeInsets.only(left: 15, right: 15),
height: ScreenUtil().setHeight(243),
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'http://fdfs.xmcdn.com/group63/M0A/99/95/wKgMaFz_RD-BDyFjAAIO0iRtj0U176.jpg'),
fit: BoxFit.fitWidth,
alignment: Alignment.topCenter,
)),
),
16.FlutterListView嵌套GridView滚动冲突问题
ListView和GirdView都是滚动Widget 两个部件嵌套就会存在滚动冲突,解决办法如下
body: new ListView(
shrinkWrap: true,
padding: EdgeInsets.all(0),
children: <Widget>[
new GridView.count(
padding: EdgeInsets.all(0),
physics: new NeverScrollableScrollPhysics(),//增加
shrinkWrap: true,//增加
crossAxisCount: 3,
children:<Widget>[]
],
),
① 处理listview嵌套报错
shrinkWrap: true,
②处理GridView中滑动父级Listview无法滑动
physics: new NeverScrollableScrollPhysics();
17.Flutter 自定义TabBar和修改indiactor 宽度
1. 关键代码
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class FriendsList extends StatefulWidget {
@override
_FriendsListState createState() => _FriendsListState();
}
class _FriendsListState extends State<FriendsList>
with SingleTickerProviderStateMixin {
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(initialIndex: 0, length: 2, vsync: this);
}
@override
void dispose() {
super.dispose();
_tabController.dispose();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Container(
height: ScreenUtil().setHeight(73),
alignment: Alignment.topLeft,
child: TabBar(
tabs: [
Tab(text: '好友'),
Tab(text: '心动'),
],
controller: _tabController,
indicatorWeight: 2,
indicatorPadding: EdgeInsets.only(left: 10, right: 10),
labelPadding: EdgeInsets.symmetric(horizontal: 10),
isScrollable: true,
indicatorColor: Color(0xffFF7E98),
labelColor: Color(0xffFF7E98),
labelStyle: TextStyle(
fontSize: ScreenUtil().setSp(36),
color: Color(0xffFF7E98),
fontWeight: FontWeight.w500,
),
unselectedLabelColor: Color(0xffAAAAAA),
unselectedLabelStyle: TextStyle(
fontSize: ScreenUtil().setSp(32), color: Color(0xffAAAAAA)),
indicatorSize: TabBarIndicatorSize.label,
)),
backgroundColor: Colors.white,
elevation: 0,
),
body: TabBarView(
children: [
Container(
child: Center(
child: Text("好友页面"),
),
),
Container(
child: Center(
child: Text("心动页面"),
),
),
],
controller: _tabController,
),
),
);
}
}
2. 效果图
18 fluro 插件 实现appBar不要出现返回键
Application.router.navigateTo(context, "/index",replace: true);
19.文字溢出处理
①Expanded + TextOverflow.ellipsis 设置省略号
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("LayoutPage")),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.star,
size: 16.0,
color: Colors.grey,
),
Padding(padding: new EdgeInsets.only(left: 5.0)),
Expanded(
child: Text(
"100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000",
style: new TextStyle(color: Colors.grey, fontSize: 14.0),
// 设置省略号
overflow: TextOverflow.ellipsis,
// 设置最大行数
maxLines: 1,
),
)
],
),
),
);
}
② Expanded + TextOverflow.ellipsis 不生效
通过
- 限定Container 宽度
- Row 布局嵌套 Expanded 可以添加约束
20.数据解析报错之关键字 do
do 为关键字,不能设置为Model的属性,应该用其他名称替换
class UserChatList {
int doType;
UserChatList({this.doType});
UserChatList.fromJson(Map<String, dynamic> json) {
doType = json['do'];
}
21.聊天消息UI搭建
效果图
思路
- ①Positison 设置
right: ScreenUtil().setWidth(20),
bottom: -ScreenUtil().setHeight(50),
- ② Stack 设置溢出显示
overflow: Overflow.visible
关键代码
/// 聊天Widget
Widget ChatWidget(String chatType, String msg) {
// 1 发出者
if (chatType == 'send') {
return Container(
decoration: BoxDecoration(
color: Colors.white,
// 设置阴影
boxShadow: [
BoxShadow(
color: Color(0xffFF7E98),
offset: Offset(0, 1),
blurRadius: 8,
)
],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
bottomRight: Radius.circular(15)),
),
height: ScreenUtil().setHeight(80),
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(40),
),
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(40),
right: ScreenUtil().setWidth(40),
top: ScreenUtil().setHeight(10),
bottom: ScreenUtil().setHeight(10)),
child: Text(
msg != null && msg.length > 0 ? msg : '',
style: TextStyle(
fontSize: ScreenUtil().setSp(30), color: Color(0xff333333)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
} else if (chatType == 'minisend') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Stack(
overflow: Overflow.visible,
children: <Widget>[
// 消息
Container(
decoration: BoxDecoration(
color: Colors.white,
// 设置阴影
boxShadow: [
BoxShadow(
color: Color(0xffFF7E98),
offset: Offset(1, 1),
blurRadius: 8,
)
],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
bottomRight: Radius.circular(15)),
),
height: ScreenUtil().setHeight(80),
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(40),
),
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(40),
right: ScreenUtil().setWidth(40),
top: ScreenUtil().setHeight(10),
bottom: ScreenUtil().setHeight(10)),
child: Text(
'😊很想认识你😊',
style: TextStyle(
fontSize: ScreenUtil().setSp(30),
color: Color(0xff333333)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Positioned(
right: ScreenUtil().setWidth(20),
bottom: -ScreenUtil().setHeight(50),
child: // 小程序路径
Container(
margin: EdgeInsets.only(top: ScreenUtil().setHeight(16)),
child: Row(
children: <Widget>[
Text(
'去小程序查看',
style: TextStyle(
fontSize: ScreenUtil().setSp(22),
color: Color(0xffFF7E98)),
),
Icon(
MyIcons.sex_boy,
size: ScreenUtil().setSp(16),
color: Color(0xffFF7E98),
)
],
),
),
),
],
),
],
);
} else if (chatType == 'mine') {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
// 设置阴影
boxShadow: [
BoxShadow(
color: Color(0xffFF7E98),
offset: Offset(0, 1),
blurRadius: 8,
)
],
// 设置渐变色
gradient: LinearGradient(
colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
begin: Alignment(-1, -1),
end: Alignment(1.0, 0.56),
),
// 设置圆角
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
bottomLeft: Radius.circular(15)),
),
height: ScreenUtil().setHeight(80),
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(40),
),
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(40),
right: ScreenUtil().setWidth(40),
top: ScreenUtil().setHeight(10),
bottom: ScreenUtil().setHeight(10)),
child: Text(
msg != null && msg.length > 0 ? msg : '',
style: TextStyle(
fontSize: ScreenUtil().setSp(30), color: Colors.white),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
)
],
);
} else {
return Container(
margin: EdgeInsets.only(bottom:ScreenUtil().setHeight(40) ),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Stack(
overflow: Overflow.visible,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
// 设置阴影
boxShadow: [
BoxShadow(
color: Color(0xffFF7E98),
offset: Offset(0, 1),
blurRadius: 8,
)
],
// 设置渐变色
gradient: LinearGradient(
colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
begin: Alignment(-1, -1),
end: Alignment(1.0, 0.56),
),
// 设置圆角
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
bottomLeft: Radius.circular(15)),
),
height: ScreenUtil().setHeight(80),
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(40),
),
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(40),
right: ScreenUtil().setWidth(40),
top: ScreenUtil().setHeight(10),
bottom: ScreenUtil().setHeight(10)),
child: Text(
msg != null && msg.length > 0 ? msg : '',
style: TextStyle(
fontSize: ScreenUtil().setSp(30), color: Colors.white),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
// 小程序路径
Positioned(
left: ScreenUtil().setWidth(20),
bottom: -ScreenUtil().setHeight(50),
child: Container(
margin: EdgeInsets.only(top: ScreenUtil().setHeight(16),),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
'去小程序查看',
style: TextStyle(
fontSize: ScreenUtil().setSp(22),
color: Color(0xffFF7E98)),
),
Icon(
MyIcons.sex_boy,
size: ScreenUtil().setSp(16),
color: Color(0xffFF7E98),
)
],
),
),
),
],
)
],
),
);
}
}
22. Flutter 复制到剪切板
通过Clipboard实现复制操作
1.声明key并在Scaffold指定key
/// 剪切板Key
final clicpBoardKey = new GlobalKey<ScaffoldState>();
return Scaffold(
key: clicpBoardKey,
);
2.实现复制操作并弹出SnackBar
Clipboard.setData(ClipboardData(text: '人生若只初相见'));
clicpBoardKey.currentState.showSnackBar(SnackBar(content: Text('已复制到剪贴板')));
其他
Scaffold.of(context).showSnackBar(SnackBar(
//提示信息内容部分
content: Text("显示SnackBar"),
));
23.Url 转义 decode
decodeURIComponent('%2Fpage%2Forigin%2Forigin%3Fuid%3D')
24.获取widget 控件的尺寸
- 通过Context
- 通过GlobalKey
- 通过LayoutChangedNotification(重写)
// 宽度
width: MediaQuery.of(context).size.width,
// 高度
height: MediaQuery.of(context).size.height * 0.05,
// 注意: context 为父组件的context
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class NewSizeChangedLayoutNotification extends LayoutChangedNotification{
Size size;
NewSizeChangedLayoutNotification(this.size);
}
class NewSizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
const NewSizeChangedLayoutNotifier({
Key key,
Widget child,
}) : super(key: key, child: child);
@override
_NewRenderSizeChangedWithCallback createRenderObject(BuildContext context) {
return _NewRenderSizeChangedWithCallback(
onLayoutChangedCallback: (Size size) {
NewSizeChangedLayoutNotification(size).dispatch(context);
}
);
}
}
typedef VoidCallbackWithParam = Function(Size size);
class _NewRenderSizeChangedWithCallback extends RenderProxyBox {
_NewRenderSizeChangedWithCallback({
RenderBox child,
@required this.onLayoutChangedCallback,
}) : assert(onLayoutChangedCallback != null),
super(child);
final VoidCallbackWithParam onLayoutChangedCallback;
Size _oldSize;
@override
void performLayout() {
super.performLayout();
//在第一次layout结束后就会进行通知
if (size != _oldSize)
onLayoutChangedCallback(size);
_oldSize = size;
}
}
25.decoration相关
1) 边框
// 同时设置4条边框:1px粗细的黑色实线边框
BoxDecoration(
border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)
// 设置单边框:上边框为1px粗细的黑色实线边框,右边框为1px粗细的红色实线边框
BoxDecoration(
border: Border(
top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
),
)
2) 圆角
// 同时设置4个角的圆角为5
BoxDecoration(
borderRadius: BorderRadius.circular(5),
)
// 设置单圆角:左上角的圆角为5,右上角的圆角为10
BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(10),
),
)
3) 阴影
BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(0, 0),
blurRadius: 6,
spreadRadius: 10,
color: Color.fromARGB(20, 0, 0, 0),
),
],
)
4) 渐变色
// 从左到右,红色到蓝色的线性渐变
BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.red, Colors.blue],
),
)
// 从中心向四周扩散,红色到蓝色的径向渐变
BoxDecoration(
gradient: RadialGradient(
center: Alignment.center,
colors: [Colors.red, Colors.blue],
),
)
// 设置角度
final gradient = Utils.parseAngleToAlignment(90);
BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xFFFFA3AD),
Color(0xFFFC5E72)
],
begin: Alignment(gradient['beginX'], gradient['beginY']),
end: Alignment(gradient['endX'], gradient['endY'])
),
borderRadius: BorderRadius.circular(2)
)
26.MaterialApp 使用讲解
字段 类型
home(主页) Widget
routes(路由) Map<String, WidgetBuilder>
theme(主题) ThemeData
debugShowMaterialGrid(调试显示材质网格) bool
navigatorKey(导航键) GlobalKey<NavigatorState>
onGenerateRoute(生成路由) RouteFactory
onUnknownRoute(未知路由) RouteFactory
navigatorObservers(导航观察器) List<NavigatorObserver>
initialRoute(初始路由) String
builder(建造者) TransitionBuilder
title(标题) String
onGenerateTitle(生成标题) GenerateAppTitle
color(颜色) Color
locale(地点) Locale
localizationsDelegates(本地化委托) Iterable<LocalizationsDelegate<dynamic>>
localeResolutionCallback(区域分辨回调) LocaleResolutionCallback
supportedLocales(支持区域) Iterable<Locale>
showPerformanceOverlay(显示性能叠加) bool
checkerboardRasterCacheImages(棋盘格光栅缓存图像) bool
checkerboardOffscreenLayers(棋盘格层) bool
showSemanticsDebugger(显示语义调试器) bool
debugShowCheckedModeBanner(调试显示检查模式横幅) bool
27.使用FutureBuilder
每调用一次setState就会重新请求future
解决方法:将 future
提取出来,作为一个变量
Future<int> future;
@override
void initState() {
super.initState();
future=getInt();
}
FutureBuilder<int>(
future: future,
builder: (context, snapshot) {
return ...;
}
),
Future<int> getInt(){
return Future.value(1);
}
28.输入框内容为空时,长按不显示粘贴工具栏
将输入框中的autoFocus属性为ture去掉
29.Flutter 左上角返回按钮回调(CallBack)
1.1 async await 实现
/// 跳转到下级页面时 await Navigator.pushNamed
onTap: () async {
await Navigator.pushNamed(context, '/account');
//执行 刷新数据操作
refrshData();
},
2.嵌套封装 会导致await 失效
class NavigatorUtil{
/// 通用跳转
static push(BuildContext context,Widget widget ) {
Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation){
return new FadeTransition( //使用渐隐渐入过渡,
opacity: animation,
child:widget,
);
})
);
}
}
//使用导致await失效
onTap: () async {
// 其他
await NavigatorUtil.push(context, widget);
//执行刷新操作
},
解决方案
封装层嵌套 async await
class NavigatorUtil{
/// 通用跳转
static push(BuildContext context,Widget widget ) async {
await Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation){
return new FadeTransition( //使用渐隐渐入过渡,
opacity: animation,
child:widget,
);
})
);
}
}
30.GestureDetector 手势冲突
解决手势冲突 - IgnorePointer
IgnorePointer(
child: GestureDetector(
child: Container(
height: ScreenUtil().setHeight(300),
width: Screen.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(ScreenUtil().setWidth(20)),
topRight: Radius.circular(ScreenUtil().setWidth(20)),
),
gradient: LinearGradient(
colors: [
Color(0xFFFFFFFF),
Colors.white.withOpacity(.2),
Colors.white.withOpacity(0),
Colors.white.withOpacity(0),
Colors.white.withOpacity(0)
],
begin: Alignment(
topGradient['beginX'], topGradient['beginY']),
end: Alignment(topGradient['endX'], topGradient['endY']),
),
),
),
onTap: () {
backToTop();
},
),
),
31.TextField 设置border 颜色(黑线修改颜色)
/// 输入框
Container(
child: Theme(
data: ThemeData(
primaryColor: Colors.white, hintColor: Colors.white),
child: TextField(
style: TextStyle(
fontSize: ScreenUtil().setSp(36),
color: Colors.white,
),
controller: inputController,
onChanged: handlePhoneInput,
autofocus: true,
decoration: new InputDecoration(
border: const UnderlineInputBorder(
borderSide: BorderSide(style: BorderStyle.solid,color: Colors.white,),
),
contentPadding: EdgeInsets.only(
left: ScreenUtil().setWidth(100),
right: ScreenUtil().setWidth(20),
top: ScreenUtil().setWidth(20),
bottom: ScreenUtil().setWidth(20),
),
hintText: '输入手机号',
hintStyle: TextStyle(
color: Color.fromRGBO(255, 255, 255, .7),
fontSize: ScreenUtil().setSp(36),
),
),
),
),
),
32.decoration 阴影设置无边界
通过Opacity 以及 LinearGradient设置 stops节点和colors 结合
// 顶部阴影
Opacity(
opacity: 0.23,
child: Container(
height: ScreenUtil().setHeight(129),
decoration: BoxDecoration(
gradient: LinearGradient(
stops: [
0,
.8
],
colors: [
Color(0xff565656),
Color(0xFF030303).withOpacity(0),
],
begin:
Alignment(gradient['beginX'], gradient['beginY']),
end: Alignment(gradient['endX'], gradient['endY'])),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
child: Container(
margin: EdgeInsets.only(
left: ScreenUtil().setWidth(10),
right: ScreenUtil().setWidth(17),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(
top: ScreenUtil().setHeight(17)),
child: Row(
children: <Widget>[
Icon(
MyIcons.heart,
size: ScreenUtil().setSp(40),
color: Colors.white,
),
Container(
margin: EdgeInsets.only(
left: ScreenUtil().setWidth(5),
top: ScreenUtil().setWidth(5),
),
child: Text(
this.widget.item != null &&
this.widget.item.praises != null
? this.widget.item.praises.toString()
: '',
style: TextStyle(
fontSize: ScreenUtil().setSp(20),
color: Colors.white,
),
textAlign: TextAlign.center,
),
)
],
),
),
],
),
),
),
)
33.Dart List.asMap() 获取下标
this.list.asMap().keys.map((i) {
// i 为下标
return _itemUI(context, i);
}).toList()
34.indexWhere 获取数组索引
int currentIndex = this.renderList.indexWhere((item) => item.id == feed.id);
35.build runner 插件使用
build runner 插件编译生成属性快捷键
flutter packages run build_runner build --delete-conflicting-outputs
36.Container点击区域过小
GestureDetector 内Container不设置color点击区域会根据内容大小来定
37.xcrun instruments 打开模拟器
xcrun instruments -w "iPhone 8 Plus (13.1)"
39. GestureDetector处理手势操作 behavior 行为
HitTestBehavior.opaque
自己处理事件HitTestBehavior.deferToChild
child处理事件HitTestBehavior.translucent
自己和child都可以接收事件
40.Widget无法居中,对齐
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: ScreenUtil().setHeight(114),
height: ScreenUtil().setHeight(114),
margin: EdgeInsets.only(
left: ScreenUtil().setWidth(10),
),
child: Center(child: FailedDot(),),
)
: Container()
],
),
41.Flutter Container 点击区域太小
使用GestureDetector包裹Container,发现在Container内容为空的区域点击时,捕捉不到onTap点击事件。
解决方案
:在GestureDetector里面添加属性:behavior: HitTestBehavior.opaque,
即可:
GestureDetector(
behavior: HitTestBehavior.opaque,
child: Container( width: ScreenUtil().setHeight(114),
height: ScreenUtil().setHeight(114),child:Text('点我')),
onTap: () {
this.handlePlayVoice();
},
)
42.监听页面返回事件(返回按钮点击+侧滑返回)
侧滑不会触发onBack回调,因此使用WillPopScope
的onWillPop
来实现
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// 设置草稿箱
this.setCraft();
return true;
},
child: Container()
}
43PageView使用注意事项
问题描述:第一次指定加载第二个page,切换时需要切换两次才显示正常
原因分析:
PageView未初始化时默认index = 0,你强行修改时会导致两个index不一致
解决办法:
_controller = PageController(initialPage: currentIndex);
/// 切换
_controller.animateToPage(
currentIndex,
duration: Duration(
milliseconds:
(pageSwitchAnimatedTime + 100),
),
curve: Curves.ease,
);
44 ClipOval组件详解
- 圆形裁剪(超出部分隐藏,相当于Stack的overFlow为clip)
Center(
child: ClipOval(
child: Image.asset(
"images/app.png",
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
),
)
45如何判断设备是否为iPad
// 1.导入设备信息插件
# 设备信息
device_info: ^0.4.0
// 2. 使用
Future<bool> isIpad() async{
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo info = await deviceInfo.iosInfo;
if (info.name.toLowerCase().contains("ipad")) {
return true;
}
return false;
}
46 setState导致的内存泄漏——setState() called after dispose()
错误原因
- 定时器没有被销毁(dispose 销毁cancel)
解决方法
临时方案
- 异步消息未返回,所以在
setState
方法之前调用mouted
属性进行判断即可
if (mounted) {
setState(() {
//refreshData
});
}
最终解决方案
@override
void dispose() {
_countdownTimer?.cancel();
super.dispose();
}
47.Flutter 设置圆角图片
通过 ClipRRect
+ borderRadius
实现
Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
this.matchedUser.user.avatar,
fit: BoxFit.cover,
),
),
width: AdaptationUtils.px(140),
height: AdaptationUtils.px(140),
)
48.clamp 语法
顾名思义,夹紧,所得的结果不会超出这个范围,类似于闭区间[]
例如:
int origen = 10;
int result = origen.clamp(2, 11);
print(result);//MARK:结果为10
//////////////////////////////////
int origen = 10;
int result = origen.clamp(2, 9);
print(result);//MARK:结果为9
49.Flutter 扩大点击区域(Container)
效果图
代码封装
/// 扩大点击区域(Container)
///
/// created by hujintao
/// created at 2020-03-19
//
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class EnlargeWrapper extends StatefulWidget {
Widget child;
EdgeInsetsGeometry enlarge;
EnlargeWrapper({
this.child,
this.enlarge,
});
@override
_EnlargeWrapperState createState() => _EnlargeWrapperState();
}
class _EnlargeWrapperState extends State<EnlargeWrapper> {
@override
Widget build(BuildContext context) {
return Container(
child: this.widget.child,
padding: this.widget.enlarge ??
EdgeInsets.fromLTRB(
ScreenUtil().setWidth(40),
ScreenUtil().setWidth(40),
ScreenUtil().setWidth(40),
ScreenUtil().setWidth(40),
),
// color: Colors.yellow,//测试
color: Colors.transparent,
);
}
}
使用
Center(
child: GestureDetector(
child: EnlargeWrapper(
child: Container(
width: 100,
height: 100,
color: Colors.red,
child: Center(
child: Text('点我s撒'),
),
),
enlarge: EdgeInsets.all(50),
),
onTap: () {
Toast.show('点击了');
},
),
)
50.根据名称读取文件
//根据名称读取文件
import 'dart:io';
readFile(name) async {
//创建文件对象
var file = File(name);
try {
//判断是否存在
bool exists = await file.exists();
if (exists) {
//如果存在
print(await file.length()); //文件大小(字节)---137
print(await file.lastModified()); //最后修改时间---2018-12-21 13:49:35.000
print(file.parent.path); //获取父文件夹的路径---C:\Users\Administrator\Desktop\dart
return await file.readAsString(); //读取文件并返回
} else {
await file.create(recursive: true); //不存在则创建文件
return "未发现文件,已为您创建!Dart机器人:2333";
}
} catch (e) {
//异常处理
print(e);
}
}
使用
var result = await readFile("\Users\XXX\Desktop\dart\test.txt");
print(result);
添加所有SD卡文件名称
// 添加所有SD卡文件名称
localPath() {
try {
var perm =
SimplePermissions.requestPermission(Permission.ReadExternalStorage);
var sdPath = getExternalStorageDirectory();
sdPath.then((file) {
perm.then((v) {
file.list().forEach((i) {
_files.add(i.path);
});
setState(() {});
});
});
} catch (err) {
print(err);
}
}
51 Flutter 微信分享和支付回调
通过 fluwx 微信SDK插件
下载插件
# 微信sdk
fluwx: ^1.2.1+1
1.微信分享回调
import 'package:fluwx/fluwx.dart' as fluwx;
/// 微信分享监听
StreamSubscription wxShareListener;
@override
void initState() {
super.initState();
this.track();
initWxShareListener();
}
/// 初始化微信分享监听
void initWxShareListener() {
wxShareListener =
fluwx.responseFromShare.listen(this.onWxShareResponse);
}
/// 微信分享响应
void onWxShareResponse(fluwx.WeChatShareResponse response) {
print(
'pay success: ${response.errStr}, ${response.type}, ${response.errCode}, ${response.androidOpenId}');
if (response.errCode == 0) {
Toast.show('分享回来了额~~~~');
}
}
// 销毁监听
@override
void dispose() {
wxShareListener.cancel();
super.dispose();
}
2.微信支付回调
import 'package:fluwx/fluwx.dart' as fluwx;
/// 微信支付监听
StreamSubscription fluwxPaymentListener;
@override
void initState() {
super.initState();
this.track();
initWxPayListener();
}
/// 初始化微信支付监听
void initWxPayListener() {
fluwxPaymentListener =
fluwx.responseFromPayment.listen(this.onWxPayResponse);
}
/// 微信支付响应
void onWxPayResponse(fluwx.WeChatPaymentResponse response) {
print(
'pay success: ${response.errStr}, ${response.type}, ${response.errCode}, ${response.androidOpenId}');
// 微信响应之后, 不允许关闭窗口
if (this.canClose) {
this.canClose = false;
setState(() {});
}
if (response.errCode == 0) {
this.checkOrderStatus();
} else {
if (response.errCode == -1) {
this.onError(PayErrorEnum.WxPayError);
} else if (response.errCode == -2) {
this.onError(PayErrorEnum.WxPayCanceled);
} else {
this.onError(PayErrorEnum.WxPayUnKnowError);
}
}
}
// 销毁监听
@override
void dispose() {
fluwxPaymentListener.cancel();
super.dispose();
}
52.数据库操作抽象类(接口封装)
//数据库操作抽象类
abstract class DateBaseOperate {
void insert(); //定义插入的方法
void delete(); //定义删除的方法
void update(); //定义更新的方法
void query(); //定义一个查询的方法
}
//数据库操作实现类
class DateBaseOperateImpl extends DateBaseOperate {
//实现了插入的方法
void insert(){
print('实现了插入的方法');
}
//实现了删除的方法
void delete(){
print('实现了删除的方法');
}
//实现了更新的方法
void update(){
print('实现了更新的方法');
}
//实现了一个查询的方法
void query(){
print('实现了一个查询的方法');
}
}
main() {
var db = new DateBaseOperateImpl();
db.insert();
db.delete();
db.update();
db.query();
}
53 .继承
//动物类
class Animal {
//动物会吃
void eat(){
print('动物会吃');
}
//动物会跑
void run(){
print('动物会跑');
}
}
//人类
class Human extends Animal {
//人类会说
void say(){
print('人类会说');
}
//人类会学习
void study(){
print('人类会学习');
}
}
main() {
print('实例化一个动物类');
var animal = new Animal();
animal.eat();
animal.run();
print('实例化一个人类');
var human = new Human();
human.eat();
human.run();
human.say();
human.study();
}
54.流程控制语句
void test(){
//if else 示例
// String today = 'Monday';
// if (today == 'Monday') {
// print('今天是星期一');
// } else if (today == 'Tuesday') {
// print('今天是星期二');
// } else {
// print('今天是个好日子');
// }
//for循环示例
var message = new StringBuffer("Hello Dart");
for (var i = 0; i < 5; i++) {
message.write('!');
}
print(message);
//forEach示例
// var arr = [0, 1, 2, 3, 4, 5, 6];
// for (var v in arr) {
// print(v);
// }
//while循环示例
// var _temp = 0;
// while(_temp < 5){
//
// print("这是一个循环: " + (_temp).toString());
// _temp ++;
// }
//do-while循环示例
// var _temp = 0;
//
// do{
// print("这是一个循环: " + (_temp).toString());
// _temp ++;
// }
// while(_temp < 5);
//break continue示例
var arr = [0, 1, 2, 3, 4, 5, 6];
for (var v in arr) {
if(v == 2 ){
//break;
continue;
}
print(v);
}
//switch case示例
String today = 'Monday';
switch (today) {
case 'Monday':
print('星期一');
break;
case 'Tuesday':
print('星期二');
break;
}
//异常处理示例
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
} finally {
print('Do some thing:\n');
}
}
54 .Getters 和 Setters 方法
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
//获取right值
num get right => left + width;
//设置right值 同时left也发生变化
set right(num value) => left = value - width;
//获取bottom值
num get bottom => top + height;
//设置bottom值 同时top也发生变化
set bottom(num value) => top = value - height;
}
55. Text设置下划线,虚线和删除线
TextStyle(
//字体颜色
color: const Color(0xffff0000),
//文本装饰器(删除线)
decoration: TextDecoration.lineThrough,
//文本装饰器颜色(删除线颜色)
decorationColor: const Color(0xff000000),
//字体大小
fontSize: 18.0,
//字体样式 是否斜体
fontStyle: FontStyle.italic,
//字体粗细
fontWeight: FontWeight.bold,
//文字间距
letterSpacing: 2.0,
)
// 下划线
color: const Color(0xffff9900),
decoration: TextDecoration.underline,
// 虚线
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.dashed,
// 斜体
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
56.PopupMenuButton组件示例
//会控菜单项
enum ConferenceItem { AddMember, LockConference, ModifyLayout, TurnoffAll }
FlatButton(
onPressed: () {},
child: PopupMenuButton<ConferenceItem>(
onSelected: (ConferenceItem result) {},
itemBuilder: (BuildContext context) =>//菜单项构造器
<PopupMenuEntry<ConferenceItem>>[
const PopupMenuItem<ConferenceItem>(//菜单项
value: ConferenceItem.AddMember,
child: Text('添加成员'),
),
const PopupMenuItem<ConferenceItem>(
value: ConferenceItem.LockConference,
child: Text('锁定会议'),
),
const PopupMenuItem<ConferenceItem>(
value: ConferenceItem.ModifyLayout,
child: Text('修改布局'),
),
const PopupMenuItem<ConferenceItem>(
value: ConferenceItem.TurnoffAll,
child: Text('挂断所有'),
),
],
),
)
57.TextField组件详解
TextField(
//绑定controller
controller: controller,
//最大长度,设置此项会让TextField右下角有一个输入数量的统计字符串
maxLength: 30,
//最大行数
maxLines: 1,
//是否自动更正
autocorrect: true,
//是否自动对焦
autofocus: true,
//是否是密码
obscureText: false,
//文本对齐方式
textAlign: TextAlign.center,
//输入文本的样式
style: TextStyle(fontSize: 26.0, color: Colors.green),
//文本内容改变时回调
onChanged: (text) {
print('文本内容改变时回调 $text');
},
//内容提交时回调
onSubmitted: (text) {
print('内容提交时回调 $text');
},
enabled: true, //是否禁用
decoration: InputDecoration(//添加装饰效果
fillColor: Colors.grey.shade200,//添加灰色填充色
filled: true,
helperText: '用户名',
prefixIcon: Icon(Icons.person),//左侧图标
suffixText: '用户名'),//右侧文本提示
)
58. 滑动删除示例
效果图
代码
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
title: '滑动删除示例',
home: new MyApp(),
));
}
class MyApp extends StatelessWidget {
//构建30条列表数据
List<String> items = new List<String>.generate(30, (i) => "列表项 ${i + 1}");
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('滑动删除示例'),
),
//构建列表
body: new ListView.builder(
itemCount: items.length,//指定列表长度
itemBuilder: (context, index) {//构建列表
//提取出被删除的项
final item = items[index];
//返回一个可以被删除的列表项
return new Dismissible(
key: new Key(item),
//被删除回调
onDismissed: (direction) {
//移除指定索引项
items.removeAt(index);
//底部弹出消息提示当前项被删除了
Scaffold.of(context).showSnackBar(
new SnackBar(content: new Text("$item 被删除了")));
},
child: new ListTile(title: new Text('$item'),)
);
},
),
);
}
}
59 自定义字体添加及使用
-
1.放置字体资源
2.在
pubspec.yaml
配置路径
fonts:
- family: myfont
fonts:
- asset: fonts/myfont.ttf
- 3.使用
Center(
child: new Text(
'你好 flutter',
style: new TextStyle(fontFamily: 'myfont',fontSize: 36.0),
),
)
- 效果图
60.页面跳转返回数据
//压栈操作并等待页面返回操作
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
//读取并显示返回值
Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));
//出栈带上参数 返回到上一个页面
Navigator.pop(context, 'hi flutter');
61.fluro 企业级路由使用
- 导入依赖
fluro: ^1.5.0
- 组件封装
// 1.定义Application类
import 'package:fluro/fluro.dart';
//定义Application类
class Application{
//使用静态变量创建Router对象
static Router router;
}
// 2.定义路由集和handler
/// 2.1 handler
import 'package:fluro/fluro.dart';
import '../pages/second_page.dart';
import 'package:flutter/material.dart';
//创建Handler用来接收路由参数及返回第二个页面对象
Handler secondPageHandler = Handler(
handlerFunc: (BuildContext context,Map<String,List<String>> params){
//读取goodId参数 first即为第一个数据
String goodId = params['goodId'].first;
return SecondPage(goodId);
}
);
/// 2.2 路由集
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'router_handler.dart';
//路由集
class Routes{
//根路径
static String root = '/';
//第二个页面路径
static String secondPage = '/secondPage';
//配置路由对象
static void configureRoutes(Router router){
//没有找到路由的回调方法
router.notFoundHandler = Handler(
handlerFunc: (BuildContext context,Map<String,List<String>> params){
print('error::: router 没有找到');
}
);
//定义第二页面路由的Handler
router.define(secondPage, handler: secondPageHandler);
}
}
- 第一个页面
import 'package:flutter/material.dart';
import '../routers/application.dart';
import 'package:fluro/fluro.dart';
//第一个页面
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Fluro路由导航示例"),
),
body: Center(
child: RaisedButton(
//点击处理
onPressed: () {
_navigateToSecondPage(context);
},
child: Text('打开第二个页面'),
),
),
);
}
//路由至第二个页面
_navigateToSecondPage(BuildContext context) async {
//路由带的参数
String goodId = '001';
//通过Application类里的路由router导航至第二个页面 可指定页面切换动画类型
Application.router.navigateTo(context, "/secondPage?goodId=$goodId",transition: TransitionType.fadeIn).then((result) {//回传值
//当第二个页面返回时带的参数为result值
if (result != null) {
print(result);
}
});
}
}
- 第二个页面
import 'package:flutter/material.dart';
//第二个页面
class SecondPage extends StatelessWidget {
//传递参数值
final String goodId;
SecondPage(this.goodId);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//显示传递参数值
Text(
'$goodId',
style: TextStyle(
fontSize: 28.0,
color: Colors.red,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
//出栈带上参数 返回至第一个页面
Navigator.pop(context, '第二个页面返回参数($goodId)');
},
child: Text('点击返回'),
),
),
],
),
),
);
}
}
- 配置路由
import 'package:flutter/material.dart';
import './routers/routes.dart';
import 'package:fluro/fluro.dart';
import './routers/application.dart';
import './pages/first_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//创建路由对象
final router = Router();
//配置路由集Routes的路由对象
Routes.configureRoutes(router);
//指定Application的路由对象
Application.router = router;
return Container(
child: MaterialApp(
title: "Fluro路由导航示例",
debugShowCheckedModeBanner: false,
//生成路由的回调函数,当导航的命名路由的时候,会使用这个来生成界面
onGenerateRoute: Application.router.generator,
//主页指定为第一个页面
home: FirstPage(),
),
);
}
}
62.数据库操作('Sqlite')
模型
//客户数据模型类
class Client {
//id
int id;
//姓名
String name;
//年龄
int age;
//性别
bool sex;
Client({this.id, this.name, this.age, this.sex,});
//将JSON数据转换成数据模型
factory Client.fromMap(Map<String, dynamic> json) => Client(
id: json["id"],
name: json["name"],
age: json["age"],
sex: json["sex"] == 1,
);
//将数据模型转换成JSON
Map<String, dynamic> toMap() => {
"id": id,
"name": name,
"age": age,
"sex": sex,
};
}
组件封装
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'client.dart';
import 'package:sqflite/sqflite.dart';
//数据库操作封装类
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
Database _database;
//获取Database对象
Future<Database> get database async {
//使用单例模式创建Database对象
if (_database != null) {
return _database;
}
_database = await initDB();
return _database;
}
//初始化数据库
initDB() async {
//获取文档目录对象
Directory documentsDirectory = await getApplicationDocumentsDirectory();
//获取默认数据库位置(在Android上,它通常是data/data/<package_name>/databases,在iOS上,它是Documents目录)
String path = join(documentsDirectory.path, "client.db");
//打开数据库 传入路径 版本号 打开完成回调函数
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
//数据库创建完成后创建Client表
await db.execute("CREATE TABLE Client ("
"id INTEGER PRIMARY KEY,"
"name TEXT,"
"age INTEGER,"
"sex BIT"
")");
});
}
//新增Client
insertClient(Client newClient) async {
final db = await database;
//获取表中最大的id再加1作为新的id
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Client");
int id = table.first["id"];
//向表中插入一条数据
var raw = await db.rawInsert(
"INSERT Into Client (id,name,age,sex)"
" VALUES (?,?,?,?)",
[id, newClient.name, newClient.age, newClient.sex]);
return raw;
}
//修改性别
updateSex(Client client) async {
final db = await database;
Client newClient = Client(
id: client.id,
name: client.name,
age: client.age,
sex: !client.sex);
//更新当前Client的性别
var res = await db.update("Client", newClient.toMap(),
where: "id = ?", whereArgs: [client.id]);
return res;
}
//更新Client
updateClient(Client newClient) async {
final db = await database;
var res = await db.update("Client", newClient.toMap(),
where: "id = ?", whereArgs: [newClient.id]);
return res;
}
//根据id获取Client
getClient(int id) async {
final db = await database;
//根据id查询表记录
var res = await db.query("Client", where: "id = ?", whereArgs: [id]);
//将查询返回的数据转换为Client对象并返回
return res.isNotEmpty ? Client.fromMap(res.first) : null;
}
//获取所有Client
Future<List<Client>> getAllClients() async {
final db = await database;
var res = await db.query("Client");
List<Client> list = res.isNotEmpty ? res.map((c) => Client.fromMap(c)).toList() : [];
return list;
}
//根据id删除Client
deleteClient(int id) async {
final db = await database;
return db.delete("Client", where: "id = ?", whereArgs: [id]);
}
//删除所有Client
deleteAll() async {
final db = await database;
db.rawDelete("Delete * from Client");
}
}
组件使用
// 获取所有数据
DBProvider.db.getAllClients(),
//根据id删除Client对象
DBProvider.db.deleteClient(item.id);
//更新性别
DBProvider.db.updateSex(item);
//随机取测试数据中的一条数据作为Client对象
Client rnd = clients[math.Random().nextInt(clients.length)];
//新增加一个Client对象
await DBProvider.db.insertClient(rnd);