Flutter常见知识点汇总

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(),
)

效果图:

PopupMenuButton

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. 效果图

效果图.gif

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 可以添加约束
TextOverflow.ellipsis 不生效

20.数据解析报错之关键字 do

do 为关键字,不能设置为Model的属性,应该用其他名称替换

class UserChatList {
  int doType;
  UserChatList({this.doType});
  UserChatList.fromJson(Map<String, dynamic> json) {
    doType = json['do'];
  }

21.聊天消息UI搭建

效果图

聊天消息U

思路

  • ①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回调,因此使用WillPopScopeonWillPop来实现

 @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)

效果图

扩大点击区域.gif

代码封装

/// 扩大点击区域(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. 滑动删除示例

效果图
滑动删除.gif
代码
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);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容