Flutter使用小結

如何啟動

啟用安卓模擬器

$ emulator -list-avds   // 返回可用安卓模擬器列表
$ emulator @AVD_name    // 啟動某個模擬設備

啟用 ios 模擬器

$ open -a simulator // 直接打開xcode 模擬器

啟動項目

$ flutter devices   // 返回可用模擬器列表
$ flutter run -d 模擬器xxx   // 啟用id為xxx的模擬器

熱更新啟動方式

$ flutter run -d 模擬器xxx --hot  // 啟用id為xxx的模擬器

不能使用 Hot Reload 的場景

  1. 代碼出現編譯錯誤的不能使用 Hot Reload
  2. 代碼更改會影響 APP 狀態(tài)的不能使用 Hot Reload
  3. 全局變量( global variables)和靜態(tài)字段(static fields)的更改不能使用 Hot Reload
// 如下的代碼:

    final sampleTable = [
    Table("T3"),
    ];
    /// 運行 App 之后倡缠,如果做了如下的更改:

    final sampleTable = [
    Table("T3"),
    Table("T10"),    // 修改這里的值
    ];
  1. main() 方法里的更改不能使用 Hot Reload
  2. 枚舉類型更改為常規(guī)的類或者常規(guī)的類變?yōu)槊杜e類型也不能使用 HotReload
  3. 修改通用類型聲明也不能使用 HotReload
// 例如拂檩,如下的例子:

    class A<T> {
    T i;
    }
// 改為:

    class A<T, V> {
    T i;
    V v;
    }

如何定義 model(6 步)

JSON 和序列化

// 1.導入庫
import 'package:json_annotation/json_annotation.dart';

// 在Dart中诀艰,可以在同一庫中訪問私有成員。 使用import可以導入庫,并且只能訪問其公共成員。 使用part/part of,可以將一個庫分成幾個文件便瑟,并且私有成員可訪問這些文件中的所有代碼。
// 2. part xx.g.dart庫番川,將在我們運行生成命令后自動生成
part 'tag.g.dart';

/// 3.裝飾器引用到涂,這個標注是告訴生成器,這個類是需要生成Model類的
@JsonSerializable()

/// 4.定義類
class Tag {
  String id;
  String tagName;

  Tag(
    this.id,
    this.tagName,
  );

  /// 5.定義 方法名為 類名.fromJson和_$類名ToJson 方法爽彤,格式如下
  factory Tag.fromJson(Map<String, dynamic> json) => _$TagFromJson(json);

  Map<String, dynamic> toJson() => _$TagToJson(this);

  /// 6.執(zhí)行命令生成json序列化代碼
  /// $ flutter packages pub run build_runner build
}

flutter packages pub run build_runner watch 在項目根目錄下運行來啟動watcher养盗。只需啟動一次觀察器,然后并讓它在后臺運行适篙,這是安全的

相關部件(Widget)使用說明

一往核、Scaffold 使用

 Scaffold({
    this.appBar,    // 頂部導航
    this.body,  // 主體
    this.drawer,    // 頁面左抽屜,從左往右抽出
    this.endDrawer, // 頁面右抽屜嚷节,從右往左抽出
    this.bottomNavigationBar,   // 底部導航
    this.backgroundColor,   // 背景色
  })

二聂儒、AppBar 使用及擴展

AppBar 設計上涵蓋了左中右下四個方向的占位,對應的屬性分別是 leading, title, actions, bottom硫痰,leading衩婚,actions 帶有默認 padding 和 margin 值

AppBar({
    this.leading,   // Widget
    this.automaticallyImplyLeading = true,  // bool 路由棧存在上一個路由的情況下,leading為返回箭頭
    this.title, // Widget效斑,通常是Text
    this.actions,   // List<Widget>
    this.bottom,    // PreferredSizeWidget非春,通常是TabBar
    this.elevation, // double 高度陰影
    this.backgroundColor,   // Color
    this.centerTitle,   // bool 居中標題
    ...
  })

定制 AppBar

首頁 AppBar 模塊 bottom 部分組成是 Tabbar + 頻道選擇,非 PreferredSizeWidget 類型的部件缓屠,實現方式是定制一個 PreferredSizeWidget奇昙,實現抽象類未實現的方法,在實現 build 的部分復合 Tabbar 和定制化的部分

class ZnTabBar extends StatefulWidget implements PreferredSizeWidget {
  /// Creates a [Size] with the given [height] and an infinite [width].
  @override
  Size get preferredSize {
    return Size.fromHeight(/* ZnTabBar所依賴的高度 */);
  }
  /// ...
   @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        Expanded(child: TabBar()),
        CustomPart()        // 定制化部分
    )
  }

三敌完、使用 GestureDetector

GestureDetector 是一個用于手勢識別的功能性組件储耐,通過它可以識別各種手勢。GestureDetector 實際上是指針事件的語義化封裝滨溉;

使用場景

1什湘、類似 Text、TextField 等部件沒有監(jiān)聽點擊的事件晦攒;
2闽撤、扁平化交互,Material 組件庫中提供了多種按鈕組件如 RaisedButton脯颜、FlatButton哟旗、OutlineButton 等,它們都是直接或間接對 RawMaterialButton 組件的包裝定制,Material 庫中的按鈕都有如下相同點:

  • 按下時都會有“水波動畫”
  • 有一個 onPressed 屬性來設置點擊回調热幔,當按鈕按下時會執(zhí)行該回調,如果不提供該回調則按鈕會處于禁用狀態(tài)讼庇,禁用狀態(tài)不響應用戶點擊绎巨;
    GestureDetector(
        child: Text('點擊'),
        onTap: () {
            // 處理點擊
        }
    )

其它,如實現雙擊蠕啄、長按场勤、拖動、滑動等

GestureDetector({
    this.onDoubleTap,   // 雙擊
    this.onLongPress,   // 長按
    this.onVerticalDragStart,// 垂直拖動
    this.onHorizontalDragStart, // 水平搬動
    this.onPanStart,    // 滑動
    this.onScaleStart,  // 縮放
    ...

四歼跟、使用 SingleChildScrollView

接收一個子組件和媳,當顯示內容超過屏幕且不超出太多的情況下使用,解決內容超出可視區(qū)域產生邊界溢出的情況哈街。

使用場景

1留瞳、線性布局下,Column 接收一組部件骚秦,但內容超出屏高產生垂直邊界溢出的情況下她倘;
2、流式布局下作箍,Row 接收一組部件硬梁,當內容超出屏寬且不折行的情況下;(可使用 Wrap 產生折行)
3胞得、期望的內容不會超過屏幕太多時荧止,視口不會包含超出屏幕尺寸太多的內容時;(可使用 ListView 或 CustomScrollView 代替)

五阶剑、使用 GridView

GridView.count

GridView.count 是 GridView 的命名構造函數跃巡,內部使用了 SliverGridDelegateWithFixedCrossAxisCount,通過它可以快速的創(chuàng)建橫軸固定數量子元素的 GridView

GridView.count(
    crossAxisCount: 2,  // 橫軸子元素的數量
    crossAxisSpacing: 1.0,  // 橫軸方向子元素的間距
    mainAxisSpacing: 1.0,   // 主軸方向的間距
    children: buildGridList(),
    primary:  true // 如果為 true个扰,即使?jié)L動視圖沒有足夠的內容滾動視圖也是可滾動的瓷炮,默認為 false。
    shrinkWrap: true, // shrinkWrap 常用于內容大小不確定情況递宅,如果是無界約束娘香,則 shrinkWrap 必須為 true。
)

六办龄、CustomScrollView

CustomScrollView 是可以使用 Sliver 來自定義滾動模型(效果)的組件烘绽,這里 Sliver 通常指可滾動組件子元素。

使用場景

1俐填、包含 類似 GridView 和 ListView 交互安接,同時要求整個頁面的滑動效果是統(tǒng)一的;
2、需要結合 FlexibleSpaceBar 實現 Material Design 中頭部伸縮英融;

  CustomScrollView(
    controller: _scrollController,
    slivers: <Widget>[
      // AppBar盏檐,包含一個導航欄
      SliverAppBar(
        title: Text(...)   // Widget
      ),
      // GridView
      SliverGrid(
        /// 配置同GridView
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 250.0,
          mainAxisSpacing: 1.0,
          crossAxisSpacing: 1.0,
          childAspectRatio: 1.0,
        ),
        delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            // 返回Widget
          },
          childCount: ..., // 列表長度
        ),
      ),
      //List
      new SliverFixedExtentList(
        itemExtent: 50.0,
        delegate: new SliverChildBuilderDelegate(
                (BuildContext context, int index) {
            //創(chuàng)建列表項
            return new Container(
              alignment: Alignment.center,
              color: Colors.lightBlue[100 * (index % 9)],
              child: new Text('list item $index'),
            );
          },
          childCount: 50 //50個列表項
        ),
      ),
  )

七歇式、動畫

1、繼承 SingleTickerProviderStateMixin 和 TickerProviderStateMixin胡野,通過將 SingleTickerProviderStateMixin 添加到類定義中材失,可以將 stateful 對象作為 vsync 的值;
2硫豆、兩個對象:Animation 和 AnimationController龙巨;
3、Animation 生成指導動畫的值熊响,AnimationController 管理 Animation旨别;
4、當創(chuàng)建一個 AnimationController 時汗茄,需要傳遞一個 vsync 參數秸弛,存在 vsync 時會防止屏幕外動畫消耗不必要的資源;
5洪碳、使用 Listeners 和 StatusListeners 監(jiān)聽動畫狀態(tài)改變胆屿;
6、釋放資源

vsync 做了什么事偶宫?

vsync 對象會綁定動畫的定時器到一個可視的 widget非迹,所以當 widget 不顯示時,動畫定時器將會暫停纯趋,當 widget 再次顯示時憎兽,動畫定時器重新恢復執(zhí)行,這樣就可以避免動畫相關 UI 不在當前屏幕時消耗資源吵冒。

使用

/// 1.定義類
class ZNAnimation extends StatefulWidget {
  static final String routeName = 'animaton';

  @override
  _ZNAnimation createState() => _ZNAnimation();
}
class _ZNAnimation extends State<ZNAnimation> with SingleTickerProviderStateMixin {

}

/// 2.聲明
  AnimationController controller;
  Animation<double> animation;

/// 3.初始化并設置動畫監(jiān)聽
  @override
  initState() {
    super.initState();

    controller = AnimationController(
        duration: const Duration(milliseconds: 100), vsync: this);
    // 圖片寬高從0變到300
    animation = Tween(begin: 30.0, end: 25.0).animate(controller);

    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        // 動畫執(zhí)行結束時反向執(zhí)行動畫
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        // 動畫恢復到初始狀態(tài)時執(zhí)行動畫(正向)
        setState(() {
          ...
        });
      }
    });
  }

/// 4. 使用
@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: animation,
    builder: (BuildContext context, Widget child) {
      return new Container(
          height: animation.value,
          width: animation.value,
          child: xxx, // 接收外部child再由AnimatedBuilder返回并插入widget樹中
      );
    },
    child: xxx    // 外部Widget對象
  )
}

/// 5.釋放資源

```dart
@override
dispose() {
  //路由銷毀時需要釋放動畫資源
  controller.dispose();
  super.dispose();
}

八纯命、其它部件

1、底部彈窗 showModalBottomSheet痹栖,
2亿汞、尺寸限制類容器 ConstrainedBox
3揪阿、裝飾容器 DecorateBox 可以在其子組件繪制前(或后)繪制一些裝飾(Decoration)疗我,如背景、邊框南捂、漸變等吴裤。
4、剪裁 ClipRRect 將子組件剪裁為圓角矩形

Effective Dart 及項目開發(fā)約定

Effective Dart: 最佳實踐

一溺健、dart 規(guī)范補充

1麦牺、要 盡可能的使用集合字面量來定義集合。

// 好的范例
var points = [];
var addresses = {};

// 壞的范例
var points = new List();
var addresses = new Map();

如果有必要還可以提供泛型類型。

// 好的范例
var points = <Point>[];
var addresses = <String, Address>{};

// 壞的范例
var points = new List<Point>();
var addresses = new Map<String, Address>();

2剖膳、不要 使用 .length 來判斷集合是否為空魏颓。
Dart 提供了更加高效率和易用的 getter 函數: .isEmpty 和.isNotEmpty。使用這些函數并不需要對結果再次取非吱晒。

/// 好的范例
if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');

/// 壞的范例
if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');

3琼开、使用 map().toList() 或者 map().toSet() 來 強制立刻執(zhí)行 map 的方法

map() 函數返回的對象也是一個 Iterable,該對象是懶求值(lazily evaluated) 的枕荞,只有當訪問里面的值的時候, map 的方法才被調用搞动。

4躏精、普通方法使用位置參數定義默認值

  queryCommentList(String videoId, [int pageSize = 15, int pageNo = 1])  {
    // ...
  }

  // 調用
  queryCommentList('videoId');

5、類構造方法使用命名參數

class ZNVideoPlay extends StatefulWidget {
  final Video video;

  ZNVideoPlay({this.video});

  @override
  State<StatefulWidget> createState() => _VideoPlay();
}

// 調用
  ZNVideoPlay(video: widget.video),

二鹦肿、文件或庫引入方式

直接使用庫導出對象矗烛,Flutter 編輯器插件自動導入庫絕對路徑;

組件通信的兩種方式:

一箩溃、InheritedWidget使用(3 步)

// 1瞭吃、定義數據存儲部件
class ShareDataWidget extends InheritedWidget {
  ShareDataWidget({@required this.data, Widget child}) : super(child: child);

  final String data; //需要在子樹中共享的數據,保存點擊次數

  //定義一個便捷方法涣旨,方便子樹中的widget獲取共享數據
  static ShareDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }

  //該回調決定當data發(fā)生變化時歪架,是否通知子樹中依賴data的Widget
  @override
  bool updateShouldNotify(ShareDataWidget old) {
    //如果返回true,則子樹中依賴(build函數中有調用)本widget
    //的子widget的`state.didChangeDependencies`會被調用
    return old.data != data;
  }
}
// 2霹陡、父Widget使用ShareDataWidget包裝子Widget
  ShareDataWidget(
    data: tagId,
    child: VideoList(),
  ))
// 3和蚪、子Widget使用并監(jiān)聽
  @override
  void didChangeDependencies() {
    _initData();
    super.didChangeDependencies();
  }

  _initData() async {
    var tagId = ShareDataWidget.of(context).data;
    // Fetch data with tagId
  }

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烹棉,隨后出現的幾起案子攒霹,更是在濱河造成了極大的恐慌,老刑警劉巖浆洗,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件催束,死亡現場離奇詭異,居然都是意外死亡伏社,警方通過查閱死者的電腦和手機抠刺,發(fā)現死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摘昌,“玉大人矫付,你說我怎么就攤上這事〉谘妫” “怎么了买优?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我杀赢,道長烘跺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任脂崔,我火速辦了婚禮滤淳,結果婚禮上,老公的妹妹穿的比我還像新娘砌左。我一直安慰自己脖咐,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布汇歹。 她就那樣靜靜地躺著屁擅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪产弹。 梳的紋絲不亂的頭發(fā)上派歌,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音痰哨,去河邊找鬼胶果。 笑死,一個胖子當著我的面吹牛斤斧,可吹牛的內容都是我干的早抠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼撬讽,長吁一口氣:“原來是場噩夢啊……” “哼贝或!你這毒婦竟也來了?” 一聲冷哼從身側響起锐秦,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤咪奖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后酱床,有當地人在樹林里發(fā)現了一具尸體羊赵,經...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年扇谣,在試婚紗的時候發(fā)現自己被綠了昧捷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡罐寨,死狀恐怖靡挥,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情鸯绿,我是刑警寧澤跋破,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布簸淀,位于F島的核電站,受9級特大地震影響毒返,放射性物質發(fā)生泄漏租幕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一拧簸、第九天 我趴在偏房一處隱蔽的房頂上張望劲绪。 院中可真熱鬧,春花似錦盆赤、人聲如沸贾富。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颤枪。三九已至,卻和暖如春兔乞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凉唐。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工庸追, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人台囱。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓淡溯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親簿训。 傳聞我的和親對象是個殘疾皇子咱娶,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容