Flutter常見知識點匯總

1.主題色主題色設(shè)置

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,//主題色設(shè)置,深色:則時間和電池顏色為白色;淺色:則時間和電池顏色為黑色
        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設(shè)置圓角

Row(
children: <Widget>[
  Container(
    width: 70,
    height: 70,
   // child: Image(image: AssetImage('images/Steven.png'),),//寫在此處設(shè)置圓角無效
    decoration: BoxDecoration(
      color: Colors.blue,
      borderRadius: BorderRadius.circular(10.0),
      image: DecorationImage(image:AssetImage('images/Steven.png'),
      fit: BoxFit.cover)//設(shè)置圖片的填充模式
    ),
  ),//頭像
  Container(),//右邊部分
],
)

設(shè)置圓形圖片

CircleAvatar(
          backgroundImage: new AssetImage('images/1.jpeg'),
          radius: 100.0,
        )

4.設(shè)備的寬高獲取

width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,

5.文字居中方向設(shè)置

//Container屬性
alignment: Alignment.centerLeft,

6.網(wǎng)絡圖片和本地圖片的加載

Container(
  width: 34,
  height: 34,
  margin: EdgeInsets.all(10),
  decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(6.0),
      image: DecorationImage(
        image: imageUrl != null
            ? NetworkImage(imageUrl) //網(wǎng)絡圖片
            : AssetImage(imageAssets),//本地圖片
      )),
)

7.鏈式編程-添加數(shù)據(jù)

 @override
 //數(shù)據(jù)、對象創(chuàng)建
  void initState() {
    super.initState();
    //鏈式編程,調(diào)用兩次addAllData并返回數(shù)組到 _listDatas
    _listDatas..addAll(datas)..addAll(datas);
    //數(shù)據(jù)排序
    _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();
  //計算當前位置 坐標轉(zhuǎn)換, 算出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.數(shù)組越界處理

//使用clamp
//取值范圍0~INDEX_WORDS.length-1 添加安全判斷 
int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);

10.定義回調(diào)函數(shù)和調(diào)用

//定義回調(diào)函數(shù)
final void Function(String str) indexBarCallBack;
//構(gòu)造方法
const IndexBar({Key key, this.indexBarCallBack}) : super(key: key);
//調(diào)用該callBack
//監(jiān)聽所在位置:計算當前位置
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,
    ),
  )
    //創(chuàng)建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),
          ),
        ],
      ),
    );
  }

//回調(diào)方法
  List<PopupMenuItem<String>> _buildPopupMenuItem(BuildContext context) {
    return <PopupMenuItem<String>>[
      _buildItem('images/發(fā)起群聊.png', '發(fā)起群聊'),
      _buildItem('images/添加朋友.png', '添加朋友'),
      _buildItem('images/掃一掃1.png', '掃一掃'),
      _buildItem('images/收付款.png', '收付款'),
    ];
  }

設(shè)置popup背景顏色

//MaterialApp -> theme -> cardColor
MaterialApp(
  debugShowCheckedModeBanner: false,
  title: 'Flutter Demo',//針對Android 里面可用
  theme: ThemeData(
    primarySwatch: Colors.yellow,//主題色設(shè)置,深色:則時間和電池顏色為白色;淺色:則時間和電池顏色為黑色
    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),//設(shè)置popup背景顏色
  ),
  home: RootPage(),
)

效果圖:

PopupMenuButton

12.滑動ListView讓鍵盤消失

FocusScope.of(context).requestFocus(FocusNode());

//監(jiān)聽ListView的滑動事件,讓鍵盤消失
Expanded(
    flex: 1, //占據(jù)剩余空間
    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 設(shè)置部分圓角和陰影效果

 @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(top: 10, bottom: 10),
      decoration: BoxDecoration(
        color: Colors.white,
        // 設(shè)置陰影 要在裁剪之外添加一個Containter里面處理,否則無效
        boxShadow: [
          BoxShadow(
              color: Color(0xff333333).withOpacity(0.05),
              offset: Offset(0, 1.0),
              blurRadius: 5),
        ],
      ),
      child: new ClipRRect(
        // 設(shè)置局部圓角
        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(
      "發(fā)現(xiàn)",
      style: TextStyle(
          color: Color(0xff333333), fontSize: ScreenUtil().setSp(34)),
    ),
    bottomOpacity: 0,
    elevation: 0, // 去掉底部陰影
  ),
);

其他

@override
  Widget build(BuildContext context) {
    return Scaffold(
      //頭部元素 比如:左側(cè)返回按鈕 中間標題 右側(cè)菜單
      appBar: AppBar(
        title: Text('Scaffold腳手架組件示例'),
      ),
      //視圖內(nèi)容部分 通常作為應用頁面的主顯示區(qū)域
      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圖片設(shè)置寬度自適應

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. 關(guān)鍵代碼

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 插件 實現(xiàn)appBar不要出現(xiàn)返回鍵

Application.router.navigateTo(context, "/index",replace: true);

19.文字溢出處理

①Expanded + TextOverflow.ellipsis 設(shè)置省略號

  @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),
                // 設(shè)置省略號
                overflow: TextOverflow.ellipsis,
                // 設(shè)置最大行數(shù)
                maxLines: 1,
              ),
            )
          ],
        ),
      ),
    );
  }

② Expanded + TextOverflow.ellipsis 不生效

通過

  • 限定Container 寬度
  • Row 布局嵌套 Expanded 可以添加約束
TextOverflow.ellipsis 不生效

20.數(shù)據(jù)解析報錯之關(guān)鍵字 do

do 為關(guān)鍵字,不能設(shè)置為Model的屬性,應該用其他名稱替換

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

21.聊天消息UI搭建

效果圖

聊天消息U

思路

  • ①Positison 設(shè)置
right: ScreenUtil().setWidth(20),
bottom: -ScreenUtil().setHeight(50),
  • ② Stack 設(shè)置溢出顯示
overflow: Overflow.visible

關(guān)鍵代碼

/// 聊天Widget
  Widget ChatWidget(String chatType, String msg) {
    // 1 發(fā)出者
    if (chatType == 'send') {
      return Container(
        decoration: BoxDecoration(
          color: Colors.white,
          // 設(shè)置陰影
          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,
                  // 設(shè)置陰影
                  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,
              // 設(shè)置陰影
              boxShadow: [
                BoxShadow(
                  color: Color(0xffFF7E98),
                  offset: Offset(0, 1),
                  blurRadius: 8,
                )
              ],
              // 設(shè)置漸變色
              gradient: LinearGradient(
                colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
                begin: Alignment(-1, -1),
                end: Alignment(1.0, 0.56),
              ),
              // 設(shè)置圓角
              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,
                    // 設(shè)置陰影
                    boxShadow: [
                      BoxShadow(
                        color: Color(0xffFF7E98),
                        offset: Offset(0, 1),
                        blurRadius: 8,
                      )
                    ],
                    // 設(shè)置漸變色
                    gradient: LinearGradient(
                      colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
                      begin: Alignment(-1, -1),
                      end: Alignment(1.0, 0.56),
                    ),
                    // 設(shè)置圓角
                    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實現(xiàn)復制操作

1.聲明key并在Scaffold指定key

/// 剪切板Key
final clicpBoardKey  = new GlobalKey<ScaffoldState>();
return Scaffold(
key: clicpBoardKey,
);

2.實現(xiàn)復制操作并彈出SnackBar

Clipboard.setData(ClipboardData(text: '人生若只初相見'));
clicpBoardKey.currentState.showSnackBar(SnackBar(content: Text('已復制到剪貼板')));

其他

Scaffold.of(context).showSnackBar(SnackBar(
                //提示信息內(nèi)容部分
                content: Text("顯示SnackBar"),
              ));

23.Url 轉(zhuǎn)義 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結(jié)束后就會進行通知
    if (size != _oldSize)
      onLayoutChangedCallback(size);
    _oldSize = size;
  }
}

25.decoration相關(guān)

1) 邊框

// 同時設(shè)置4條邊框:1px粗細的黑色實線邊框
BoxDecoration(
  border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)

// 設(shè)置單邊框:上邊框為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) 圓角

// 同時設(shè)置4個角的圓角為5
BoxDecoration(
  borderRadius: BorderRadius.circular(5),
)

// 設(shè)置單圓角:左上角的圓角為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],
  ),
)
// 設(shè)置角度
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(調(diào)試顯示材質(zhì)網(wǎng)格) 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(區(qū)域分辨回調(diào))    LocaleResolutionCallback
supportedLocales(支持區(qū)域)  Iterable<Locale>
showPerformanceOverlay(顯示性能疊加)  bool
checkerboardRasterCacheImages(棋盤格光柵緩存圖像)    bool
checkerboardOffscreenLayers(棋盤格層)   bool
showSemanticsDebugger(顯示語義調(diào)試器)  bool
debugShowCheckedModeBanner(調(diào)試顯示檢查模式橫幅)  bool

27.使用FutureBuilder每調(diào)用一次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.輸入框內(nèi)容為空時,長按不顯示粘貼工具欄

將輸入框中的autoFocus屬性為ture去掉

29.Flutter 左上角返回按鈕回調(diào)(CallBack)

1.1 async await 實現(xiàn)

/// 跳轉(zhuǎn)到下級頁面時 await Navigator.pushNamed
onTap: () async {
    await Navigator.pushNamed(context, '/account');
    //執(zhí)行 刷新數(shù)據(jù)操作
    refrshData();
  },

2.嵌套封裝 會導致await 失效

class NavigatorUtil{
  /// 通用跳轉(zhuǎn)
  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);
     //執(zhí)行刷新操作
  },

解決方案
封裝層嵌套 async await

class NavigatorUtil{
  /// 通用跳轉(zhuǎn)
  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 設(shè)置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 陰影設(shè)置無邊界

通過Opacity 以及 LinearGradient設(shè)置 stops節(jié)點和colors 結(jié)合

// 頂部陰影
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 獲取數(shù)組索引

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點擊區(qū)域過小

GestureDetector 內(nèi)Container不設(shè)置color點擊區(qū)域會根據(jù)內(nèi)容大小來定

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 點擊區(qū)域太小

使用GestureDetector包裹Container,發(fā)現(xiàn)在Container內(nèi)容為空的區(qū)域點擊時,捕捉不到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.監(jiān)聽頁面返回事件(返回按鈕點擊+側(cè)滑返回)

側(cè)滑不會觸發(fā)onBack回調(diào),因此使用WillPopScopeonWillPop來實現(xiàn)

 @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        // 設(shè)置草稿箱
        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如何判斷設(shè)備是否為iPad

// 1.導入設(shè)備信息插件
# 設(shè)備信息
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導致的內(nèi)存泄漏——setState() called after dispose()

錯誤原因

  • 定時器沒有被銷毀(dispose 銷毀cancel)

解決方法

臨時方案

  • 異步消息未返回,所以在setState方法之前調(diào)用mouted屬性進行判斷即可
if (mounted) {
  setState(() {
    //refreshData
  });
}

最終解決方案

@override
  void dispose() {
    _countdownTimer?.cancel();
    super.dispose();
  }

47.Flutter 設(shè)置圓角圖片

通過 ClipRRect + borderRadius 實現(xiàn)

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 語法

顧名思義,夾緊,所得的結(jié)果不會超出這個范圍,類似于閉區(qū)間[]
例如

int origen = 10;
int result = origen.clamp(2, 11);
print(result);//MARK:結(jié)果為10
//////////////////////////////////
int origen = 10;
int result = origen.clamp(2, 9);
print(result);//MARK:結(jié)果為9

49.Flutter 擴大點擊區(qū)域(Container)

效果圖

擴大點擊區(qū)域.gif

代碼封裝

/// 擴大點擊區(qū)域(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.根據(jù)名稱讀取文件

//根據(jù)名稱讀取文件
import 'dart:io';

readFile(name) async {
  //創(chuàng)建文件對象
  var file = File(name);
  try {
    //判斷是否存在
    bool exists = await file.exists();
    if (exists) {
      //如果存在
      print(await file.length()); //文件大小(字節(jié))---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); //不存在則創(chuàng)建文件
      return "未發(fā)現(xiàn)文件,已為您創(chuàng)建!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 微信分享和支付回調(diào)

通過 fluwx 微信SDK插件
下載插件

# 微信sdk
fluwx: ^1.2.1+1

1.微信分享回調(diào)

import 'package:fluwx/fluwx.dart' as fluwx;
/// 微信分享監(jiān)聽
StreamSubscription wxShareListener;
 @override
  void initState() {
    super.initState();
    this.track();
    initWxShareListener();
  }
  /// 初始化微信分享監(jiān)聽
  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('分享回來了額~~~~');
    } 
  }
  // 銷毀監(jiān)聽
  @override
  void dispose() {
    wxShareListener.cancel();
    super.dispose();
  }

2.微信支付回調(diào)

import 'package:fluwx/fluwx.dart' as fluwx;
/// 微信支付監(jiān)聽
StreamSubscription fluwxPaymentListener;
 @override
  void initState() {
    super.initState();
    this.track();
    initWxPayListener();
  }
  /// 初始化微信支付監(jiān)聽
  void initWxPayListener() {
    fluwxPaymentListener =
        fluwx.responseFromPayment.listen(this.onWxPayResponse);
  }
  /// 微信支付響應
  void onWxPayResponse(fluwx.WeChatPaymentResponse response) {
    print(
        'pay success: ${response.errStr}, ${response.type}, ${response.errCode}, ${response.androidOpenId}');

    // 微信響應之后, 不允許關(guān)閉窗口
    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);
      }
    }
  }
  // 銷毀監(jiān)聽
  @override
  void dispose() {
    fluwxPaymentListener.cancel();
    super.dispose();
  }

52.數(shù)據(jù)庫操作抽象類(接口封裝)

//數(shù)據(jù)庫操作抽象類
abstract class DateBaseOperate {

  void insert(); //定義插入的方法

  void delete(); //定義刪除的方法

  void update(); //定義更新的方法

  void query(); //定義一個查詢的方法
}

//數(shù)據(jù)庫操作實現(xiàn)類
class DateBaseOperateImpl extends DateBaseOperate {

  //實現(xiàn)了插入的方法
  void insert(){
    print('實現(xiàn)了插入的方法');
  }

  //實現(xiàn)了刪除的方法
  void delete(){
    print('實現(xiàn)了刪除的方法');
  }

  //實現(xiàn)了更新的方法
  void update(){
    print('實現(xiàn)了更新的方法');
  }

  //實現(xiàn)了一個查詢的方法
  void query(){
    print('實現(xiàn)了一個查詢的方法');
  }

}


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循環(huán)示例
    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循環(huán)示例
//    var _temp = 0;
//    while(_temp < 5){
//
//      print("這是一個循環(huán): " + (_temp).toString());
//      _temp ++;
//    }

    //do-while循環(huán)示例
//    var _temp = 0;
//
//    do{
//      print("這是一個循環(huán): " + (_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;

  //設(shè)置right值 同時left也發(fā)生變化
  set right(num value)  => left = value - width;

  //獲取bottom值
  num get bottom        => top + height;

  //設(shè)置bottom值 同時top也發(fā)生變化
  set bottom(num value) => top = value - height;
}

55. Text設(shè)置下劃線,虛線和刪除線

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) =>//菜單項構(gòu)造器
      <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,
      //最大長度,設(shè)置此項會讓TextField右下角有一個輸入數(shù)量的統(tǒng)計字符串
      maxLength: 30,
      //最大行數(shù)
      maxLines: 1,
      //是否自動更正
      autocorrect: true,
      //是否自動對焦
      autofocus: true,
      //是否是密碼
      obscureText: false,
      //文本對齊方式
      textAlign: TextAlign.center,
      //輸入文本的樣式
      style: TextStyle(fontSize: 26.0, color: Colors.green),
      //文本內(nèi)容改變時回調(diào)
      onChanged: (text) {
        print('文本內(nèi)容改變時回調(diào) $text');
      },
      //內(nèi)容提交時回調(diào)
      onSubmitted: (text) {
        print('內(nèi)容提交時回調(diào) $text');
      },
      enabled: true, //是否禁用
      decoration: InputDecoration(//添加裝飾效果
          fillColor: Colors.grey.shade200,//添加灰色填充色
          filled: true,
          helperText: '用戶名',
          prefixIcon: Icon(Icons.person),//左側(cè)圖標
          suffixText: '用戶名'),//右側(cè)文本提示
    )

58. 滑動刪除示例

效果圖
滑動刪除.gif
代碼
import 'package:flutter/material.dart';
void main() {
  runApp(new MaterialApp(
    title: '滑動刪除示例',
    home: new MyApp(),
  ));
}

class MyApp extends StatelessWidget {

  //構(gòu)建30條列表數(shù)據(jù)
  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('滑動刪除示例'),
      ),
      //構(gòu)建列表
      body: new ListView.builder(
        itemCount: items.length,//指定列表長度
        itemBuilder: (context, index) {//構(gòu)建列表

          //提取出被刪除的項
          final item = items[index];

          //返回一個可以被刪除的列表項
          return new Dismissible(
              key: new Key(item),
              //被刪除回調(diào)
              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.頁面跳轉(zhuǎn)返回數(shù)據(jù)

//壓棧操作并等待頁面返回操作
final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondPage()),
);
//讀取并顯示返回值
Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));

//出棧帶上參數(shù) 返回到上一個頁面
Navigator.pop(context, 'hi flutter');

61.fluro 企業(yè)級路由使用

  • 導入依賴
fluro: ^1.5.0
  • 組件封裝
// 1.定義Application類
import 'package:fluro/fluro.dart';

//定義Application類
class Application{
  //使用靜態(tài)變量創(chuàng)建Router對象
  static Router router;
}

// 2.定義路由集和handler
/// 2.1 handler
import 'package:fluro/fluro.dart';
import '../pages/second_page.dart';
import 'package:flutter/material.dart';

//創(chuàng)建Handler用來接收路由參數(shù)及返回第二個頁面對象
Handler secondPageHandler = Handler(
  handlerFunc: (BuildContext context,Map<String,List<String>> params){
    //讀取goodId參數(shù) first即為第一個數(shù)據(jù)
    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){

    //沒有找到路由的回調(diào)方法
    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 {
    //路由帶的參數(shù)
    String goodId  = '001';
    //通過Application類里的路由router導航至第二個頁面 可指定頁面切換動畫類型
    Application.router.navigateTo(context, "/secondPage?goodId=$goodId",transition: TransitionType.fadeIn).then((result) {//回傳值
      //當?shù)诙€頁面返回時帶的參數(shù)為result值
      if (result != null) {
        print(result);
      }
    });
  }
}
  • 第二個頁面
import 'package:flutter/material.dart';

//第二個頁面
class SecondPage extends StatelessWidget {
  //傳遞參數(shù)值
  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>[
            //顯示傳遞參數(shù)值
            Text(
              '$goodId',
              style: TextStyle(
                fontSize: 28.0,
                color: Colors.red,
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                onPressed: () {
                  //出棧帶上參數(shù) 返回至第一個頁面
                  Navigator.pop(context, '第二個頁面返回參數(shù)($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) {

    //創(chuàng)建路由對象
    final router = Router();
    //配置路由集Routes的路由對象
    Routes.configureRoutes(router);
    //指定Application的路由對象
    Application.router = router;

    return Container(
      child: MaterialApp(
        title: "Fluro路由導航示例",
        debugShowCheckedModeBanner: false,
        //生成路由的回調(diào)函數(shù),當導航的命名路由的時候,會使用這個來生成界面
        onGenerateRoute: Application.router.generator,
        //主頁指定為第一個頁面
        home: FirstPage(),
      ),
    );
  }
}

62.數(shù)據(jù)庫操作('Sqlite')

模型

//客戶數(shù)據(jù)模型類
class Client {
  //id
  int id;
  //姓名
  String name;
  //年齡
  int age;
  //性別
  bool sex;

  Client({this.id, this.name, this.age, this.sex,});

  //將JSON數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)模型
  factory Client.fromMap(Map<String, dynamic> json) => Client(
        id: json["id"],
        name: json["name"],
        age: json["age"],
        sex: json["sex"] == 1,
      );

  //將數(shù)據(jù)模型轉(zhuǎn)換成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';

//數(shù)據(jù)庫操作封裝類
class DBProvider {
  DBProvider._();

  static final DBProvider db = DBProvider._();

  Database _database;

  //獲取Database對象
  Future<Database> get database async {
    //使用單例模式創(chuàng)建Database對象
    if (_database != null) {
      return _database;
    }
    _database = await initDB();
    return _database;
  }

  //初始化數(shù)據(jù)庫
  initDB() async {
    //獲取文檔目錄對象
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    //獲取默認數(shù)據(jù)庫位置(在Android上,它通常是data/data/<package_name>/databases,在iOS上,它是Documents目錄)
    String path = join(documentsDirectory.path, "client.db");
    //打開數(shù)據(jù)庫 傳入路徑 版本號 打開完成回調(diào)函數(shù)
    return await openDatabase(path, version: 1, onOpen: (db) {},
        onCreate: (Database db, int version) async {
          //數(shù)據(jù)庫創(chuàng)建完成后創(chuàng)建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"];
    //向表中插入一條數(shù)據(jù)
    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;
  }

  //根據(jù)id獲取Client
  getClient(int id) async {
    final db = await database;
    //根據(jù)id查詢表記錄
    var res = await db.query("Client", where: "id = ?", whereArgs: [id]);
    //將查詢返回的數(shù)據(jù)轉(zhuǎn)換為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;
  }

  //根據(jù)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");
  }
}

組件使用

// 獲取所有數(shù)據(jù)
DBProvider.db.getAllClients(),
//根據(jù)id刪除Client對象
DBProvider.db.deleteClient(item.id);
//更新性別
DBProvider.db.updateSex(item);
//隨機取測試數(shù)據(jù)中的一條數(shù)據(jù)作為Client對象
Client rnd = clients[math.Random().nextInt(clients.length)];
//新增加一個Client對象
await DBProvider.db.insertClient(rnd);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容