精講Flutter官網(wǎng)的第一個例子

前言

學(xué)習(xí)Flutter你一定會看到官網(wǎng)的第一個例子:中文版英文版羡疗。但是作為新手糜颠,或許你看的會很費勁否彩,這篇文章的目的是幫助你更好的理解這個例子瑞凑。

最終的效果圖:

最終效果

我們先分析一下如何實現(xiàn)上圖中的效果:

Android開發(fā)者
<table><tr><td bgcolor=#F3F3F3 >

  1. 準(zhǔn)備數(shù)據(jù):列表數(shù)據(jù)和選中的數(shù)據(jù)可以分別使用兩個List或者數(shù)組存儲。
  2. 界面列表:使用ListView或RecyclerView
  3. 界面跳轉(zhuǎn):可以使用Intent攜帶數(shù)據(jù)到新的列表頁
    </td></tr></table>

iOS開發(fā)者
<table><tr><td bgcolor=#F3F3F3 >

  1. 準(zhǔn)備數(shù)據(jù):列表數(shù)據(jù)和選中的數(shù)據(jù)可以分別使用兩個數(shù)組存儲陡叠。
  2. 界面列表:使用TableView或CollectionView
  3. 界面跳轉(zhuǎn):使用NavigationController,可以把值直接賦值給新的頁面對象
    </td></tr></table>

結(jié)論

我們發(fā)現(xiàn)玩郊,無論是原生的Android還是iOS開發(fā),都需要做的步驟是:

  1. 存儲要展示的數(shù)據(jù)枉阵,存儲選中的數(shù)據(jù)
  2. 展示列表译红,并把數(shù)據(jù)展示出來
  3. 設(shè)置跳轉(zhuǎn)到新頁面

所以在Flutter開發(fā)中,也遵照這幾個步驟會更好的理解

Flutter開發(fā)
<table><tr><td bgcolor=#DFEFD9 >

  • 準(zhǔn)備數(shù)據(jù):列表數(shù)據(jù)使用數(shù)組存儲兴溜,選中的數(shù)據(jù)可以使用Set存儲(因為set可以自動去重)罪郊。
  • 界面列表:使用ListView
  • 界面跳轉(zhuǎn):可以使用Navigator
    </td></tr></table>

拆解分析官方代碼校仑,帶你快速理解

官網(wǎng)上使用大概110行代碼實現(xiàn)上面的例子袁串,我們把這些代碼拆解成主要的三部分來幫助我們學(xué)習(xí):

前提:你首先應(yīng)該會用Android studio或者其他開發(fā)工具創(chuàng)建一個Flutter的工程鸠真,如果你需要學(xué)習(xí)關(guān)于這個步驟,可以在 這里快速學(xué)習(xí)

當(dāng)你創(chuàng)建一個全新的Flutter工程并運行膘怕,界面上會出現(xiàn)熟悉的“Hello world”想诅。
為了更容易的理解Flutter的代碼,我們先分析一下創(chuàng)建初始的代碼淳蔼,至少要知道我們需要從哪里開始動手:

Snip20190104_19.png

我們要編輯的就是這里的 main.dart 文件侧蘸,跟其他語言一樣,F(xiàn)lutter的入口函數(shù)是main函數(shù):

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());   //分析 1

class MyApp extends StatelessWidget {  //分析 2 (StatelessWidget)
  @override
  Widget build(BuildContext context) {   //分析 3
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(      //分析 4
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(  //分析 5
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}

分析

  1. 這里的 => 是Dart中單行函數(shù)的簡寫鹉梨,等價于:
void main() {
  runApp(new MyApp());
} 
  1. StatelessWidget 代表只有一種狀態(tài)的組件讳癌,與之對應(yīng)的是StatefulWidget(表示可能有多種狀態(tài))。這里先不用深究其原理存皂,只需知道這個跟flutter的刷新等相關(guān)晌坤。

  2. 在Widget組件中都是通過build方法來描述自己的內(nèi)部結(jié)構(gòu)。這里的build表示構(gòu)建MyApp中使用的是MaterialApp的系統(tǒng)組件旦袋。

  3. home標(biāo)簽的值:Scaffold是Material library 中提供的一個組件,我們可以在里面設(shè)置導(dǎo)航欄骤菠、標(biāo)題和包含主屏幕widget樹的body屬性“淘校可以看到這里是在頁面上添加了AppBar和一個Text商乎。

  4. Center是一個可以把子組件放在中心的組件

開始改造

我們的目標(biāo)是把頁面中顯示hello_world的TextView換成一個ListView。由上面的分析可知祭阀,將上面第4點的home標(biāo)簽的值鹉戚,換成一個ListView就能改變頁面顯示的內(nèi)容鲜戒。不過在此之前,需要先準(zhǔn)備一下要顯示的數(shù)據(jù)抹凳,這里是使用一個叫 english_words 的三方包遏餐,可以幫助我們生成顯示的單詞數(shù)據(jù)。先學(xué)習(xí)一下如何添加依賴包:

  1. 打開pubspec.yaml文件添加三方庫:
dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  english_words: ^3.1.0
  1. 點擊 Packages get 獲取剛添加的包赢底。

添加english_words庫之后失都,可以這樣使用這個庫創(chuàng)造數(shù)據(jù):

//創(chuàng)造5個隨機(jī)詞組,并返回詞組的迭代器
generateWordPairs().take(5)

學(xué)習(xí)使用可變狀態(tài)的組件 StatefulWidget

查看ListView的源碼幸冻,發(fā)現(xiàn)其最終是繼承自 StatelessWidget粹庞,所以它的狀態(tài)是唯一的。但是要實現(xiàn)的ListView中的數(shù)據(jù)是動態(tài)變化的嘁扼,所以需要使用StatefulWidget來動態(tài)改變ListView中的數(shù)據(jù)信粮。

<table><tr><td bgcolor=#D1EEEE>
使用StatefulWidget組件需要自己控制在不同情況下的顯示狀態(tài)黔攒,所以需要實現(xiàn)State類來告訴StatefulWidget類不同情況下如何展示趁啸。
</td></tr></table>

創(chuàng)建一個動態(tài)變化的組件類,用于表示要顯示的ListView:

class RandomWords extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {  //分析1
    return new RandomWordsState();
  }
}

分析:

  1. 創(chuàng)建State類的方法督惰,這里繼承系統(tǒng)的State創(chuàng)造一個名叫RandomWordsState的類不傅,來控制ListView各個狀態(tài)下的展示。
class RandomWordsState extends State<RandomWords> {
}

要完成展示數(shù)據(jù)和保存點擊后的數(shù)據(jù)赏胚,這里分別用數(shù)組和set來存儲(用set存儲點擊后的數(shù)據(jù)是因為set可以去重访娶,你也可以選擇其他的存儲方式)

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];   //分析 1
  final _saved = new Set<WordPair>();
  final _biggerFont = const TextStyle(fontSize: 18.0);   //分析 2
}

分析:

  1. Dart中加上 _表示私有化
  2. 表示字體大小的常量

創(chuàng)造數(shù)據(jù)集和構(gòu)建ListView

添加如下兩個方法到RandomWordsState類中,表示創(chuàng)造數(shù)據(jù)集和構(gòu)建ListView:

Widget _buildSuggestions() {   
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // 對于每個建議的單詞對都會調(diào)用一次itemBuilder觉阅,然后將單詞對添加到ListTile行中
      // 在偶數(shù)行崖疤,該函數(shù)會為單詞對添加一個ListTile row.
      // 在奇數(shù)行,該函數(shù)會添加一個分割線widget典勇,來分隔相鄰的詞對劫哼。
      // 注意,在小屏幕上割笙,分割線看起來可能比較吃力权烧。
      itemBuilder: (context, i) {          
        // 在每一列之前,添加一個1像素高的分隔線widget
        if (i.isOdd) return new Divider();

        // 語法 "i ~/ 2" 表示i除以2伤溉,但返回值是整形(向下取整)般码,比如i為:1, 2, 3, 4, 5
        // 時,結(jié)果為0, 1, 1, 2, 2乱顾, 這可以計算出ListView中減去分隔線后的實際單詞對數(shù)量
        final index = i ~/ 2;
        // 如果是建議列表中最后一個單詞對
        if (index >= _suggestions.length) {
          // ...接著再生成10個單詞對板祝,然后添加到建議列表
          _suggestions.addAll(generateWordPairs().take(10));   
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);

    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }

代碼中有詳細(xì)的注釋,但是為了方便理解走净,這里還是給出一點解釋:

  1. _buildSuggestions方法就是返回一個ListView
  2. _buildRow方法就是返回ListView中的一行(ListTile)如何展示

_buildSuggestions方法中:

  • 代碼中的itemBuilder就是如何顯示一行(ListTile)的配置券时,其返回值是_buildRow方法
  • _suggestions.addAll(generateWordPairs().take(10));就是每次添加10個數(shù)據(jù)到顯示數(shù)組中

_buildRow方法中:

  • 設(shè)置了一行(ListTile代表一行內(nèi)容囊嘉,在iOS和android中叫cell)如何展示
  • ListTile設(shè)置了title、trailing(右邊的圖標(biāo))和點擊事件onTap()
  • 會根據(jù)有沒有被保存過革为,決定右邊顯示什么圖標(biāo)
  • 當(dāng)點擊時扭粱,會把沒有點擊過的內(nèi)容保存到_saved容器中。

添加跳轉(zhuǎn)邏輯

添加_pushSaved方法表示如何跳轉(zhuǎn)到新的頁面并展示選中的數(shù)據(jù):

main.dart

void _pushSaved() {
    Navigator.of(context).push(  // 分析 1
      new MaterialPageRoute(  // 分析 2
        builder: (context) {
          final tiles = _saved.map(  //數(shù)據(jù)
            (pair) {
              return new ListTile(
                title: new Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
              );
            },
          );
          final divided = ListTile.divideTiles(
            context: context,
            tiles: tiles,
          ).toList();

          return new Scaffold(  // 分析 3
            appBar: new AppBar(
              title: new Text('Saved Suggestions'),
            ),
            body: new ListView(children: divided),
          );
        },
      ),
    );
  }

分析:
1.使用Navigator.of(context).push的方式來處理跳轉(zhuǎn)震檩,需要的參數(shù)是一個Route
2.創(chuàng)建頁面Route
3.返回一個新的里面琢蛤,里面的body內(nèi)容是一個ListView,展示的是_saved中讀取出來的數(shù)據(jù)

最終抛虏,所有代碼整合是如下的樣子:


import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      theme: new ThemeData(
        primaryColor: Colors.red,
      ),
      home: RandomWords(),
    );
  }
}

class RandomWords extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new RandomWordsState();
  }
}

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _saved = new Set<WordPair>();
  final _biggerFont = const TextStyle(fontSize: 18.0);

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Startup Name Generator'),
        actions: <Widget>[
          new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: _buildSuggestions(),
    );
  }

  void _pushSaved() {
    Navigator.of(context).push(
      new MaterialPageRoute(
        builder: (context) {
          final tiles = _saved.map(
            (pair) {
              return new ListTile(
                title: new Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
              );
            },
          );
          final divided = ListTile.divideTiles(
            context: context,
            tiles: tiles,
          ).toList();

          return new Scaffold(
            appBar: new AppBar(
              title: new Text('Saved Suggestions'),
            ),
            body: new ListView(children: divided),
          );
        },
      ),
    );
  }

  Widget _buildSuggestions() {
    return new ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if (i.isOdd) return new Divider();
          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10));
          }
          return _buildRow(_suggestions[index]);
        });
  }

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);

    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }
}

運行吧博其,就能看到最上方的效果。

謝謝觀看這篇文章迂猴,如果讓您發(fā)現(xiàn)了錯誤或者有好的建議慕淡,歡迎在下方評論給我留言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沸毁,一起剝皮案震驚了整個濱河市峰髓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌息尺,老刑警劉巖携兵,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異搂誉,居然都是意外死亡徐紧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門炭懊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來并级,“玉大人,你說我怎么就攤上這事侮腹〕氨蹋” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵凯旋,是天一觀的道長呀潭。 經(jīng)常有香客問我,道長至非,這世上最難降的妖魔是什么钠署? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮荒椭,結(jié)果婚禮上谐鼎,老公的妹妹穿的比我還像新娘。我一直安慰自己趣惠,他們只是感情好狸棍,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布身害。 她就那樣靜靜地躺著,像睡著了一般草戈。 火紅的嫁衣襯著肌膚如雪塌鸯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天唐片,我揣著相機(jī)與錄音丙猬,去河邊找鬼。 笑死费韭,一個胖子當(dāng)著我的面吹牛茧球,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播星持,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抢埋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了督暂?” 一聲冷哼從身側(cè)響起揪垄,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎损痰,沒想到半個月后福侈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酒来,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡卢未,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了堰汉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辽社。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖翘鸭,靈堂內(nèi)的尸體忽然破棺而出滴铅,到底是詐尸還是另有隱情,我是刑警寧澤就乓,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布汉匙,位于F島的核電站,受9級特大地震影響生蚁,放射性物質(zhì)發(fā)生泄漏噩翠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一邦投、第九天 我趴在偏房一處隱蔽的房頂上張望伤锚。 院中可真熱鬧,春花似錦志衣、人聲如沸屯援。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狞洋。三九已至弯淘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吉懊,已是汗流浹背耳胎。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留惕它,地道東北人怕午。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像淹魄,于是被迫代替她去往敵國和親郁惜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353