開始使用-編寫你的第一個Flutter應用程序

這是建立您的第一個Flutter應用程序的指南。 若是您熟悉面向對象的代碼和基本編程概念(如變量,循環和條件),則能夠完成本教程。 您不須要之前使用Dart或移動編程的經驗。html

  • 第1步:建立起始Flutter應用程序
  • 第2步:使用外部包裝
  • 第3步:添加一個有狀態的小部件
  • 第4步:建立一個無限滾動ListView
  • 第5步:添加交互性
  • 第6步:導航到新的屏幕
  • 第7步:使用主題更改UI
  • 完成!

 你會創建什麼

您將實施一個簡單的移動應用程序,爲一家創業公司生成建議名稱。 用戶能夠選擇和取消選擇名稱,保存最好的名稱。 該代碼一次生成十個名稱。 當用戶滾動時,會生成新批次的名稱。 用戶能夠點擊應用欄右上方的列表圖標,以移動到僅列出收藏名稱的新路由。java

動畫GIF顯示完成的應用程序的工做方式。git

你會學到什麼:github

Flutter應用程序的基本結構。
查找和使用包來擴展功能。
使用熱重載加快開發週期。
如何實現有狀態的小部件。
如何建立一個無限的,延遲加載的列表。
如何建立並導航到第二個屏幕。
如何使用主題更改應用程序的外觀。編程

你會到用什麼:數組

您須要安裝如下內容:網絡

  • Flutter SDK
  • Flutter SDK包括Flutter的引擎,框架,小部件,工具和Dart SDK。 這個codelab須要v0.1.4或更高版本。
  • Android Studio IDE
  • 該codelab具備Android Studio IDE,但您可使用其餘IDE,或者從命令行運行。
  • 您的IDE插件
  • Flutter和Dart插件必須爲您的IDE單獨安裝。 除了Android Studio,Flutter和Dart插件也可用於VS CodeIntelliJ IDE

有關如何設置環境的信息,請參閱Flutter安裝和設置app

第1步:建立起始Flutter應用程序

使用第一個Flutter應用程序入門中的說明建立一個簡單的模板化Flutter應用程序。 將項目命名爲startup_namer(而不是myapp)。 你將會修改這個初學者應用程序來建立完成的應用程序。框架

在這個codelab中,你將主要編輯Dart代碼所在的lib / main.dart。less

提示:將代碼粘貼到應用程序中時,縮進可能會變形。 您可使用Flutter工具自動修復此問題:

  • Android Studio / IntelliJ IDEA:右鍵單擊飛鏢代碼,而後選擇Reformat Code with dartfmt格式化代碼。
  • VS代碼:右鍵單擊並選擇Format Document
  • 終端:運行flutter格式<filename>。

1.替換lib/main.dart。
刪除lib/main.dart中的全部代碼。 替換爲下面的代碼,它在屏幕的中心顯示「Hello World」。

import 'package:flutter/material.dart';

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

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

2.運行應用程序。 你應該看到下面的屏幕。

意見

  • 本示例建立一個Material應用程序。 Material是一種視覺設計語言,在移動設備和網絡上是標準的。 Flutter提供了一套豐富的Material小部件。
  • main方法指定胖箭頭(=>)表示法,它是用於單行函數或方法的簡寫。
  • 該應用程序擴展了使應用程序自己成爲小部件的StatelessWidget。 在Flutter中,大多數狀況都是一個小部件,包括對齊,填充和佈局。
  • Material庫中的Scaffold小部件提供了默認應用程序欄,標題和控制主屏幕小部件樹的body屬性。 小部件子樹可能至關複雜。
  • 小部件的主要工做是提供一個build()方法,該方法描述如何根據其餘較低級別的小部件來顯示小部件。
  • 此示例的小部件樹由包含Text小部件的Center小部件組成。 Center小部件將其小部件子樹對齊到屏幕中心。

第2步:使用外部包裝

在這一步中,您將開始使用名爲english_words的開源軟件包,其中包含數千個最經常使用的英文單詞以及一些實用功能。

您能夠在pub.dartlang.org上找到english_words軟件包以及其餘許多開源軟件包。

1.pubspec文件管理Flutter應用程序的資產。 在pubspec.yaml中,將english_words(3.1.0或更高版本)添加到依賴項列表。 新行高亮以下:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  english_words: ^3.1.0

2.在Android Studio編輯器視圖中查看pubspec時,單擊右上角的Packages get。 這將該包加入您的項目。 您應該在控制檯中看到如下內容:

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0

3.在lib/main.dart中,添加english_words導入語句,如突出顯示的行所示:

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

在您鍵入時,Android Studio會爲您提供有關庫導入的建議。 而後它將呈現灰色的導入字符串,讓您知道導入的庫還沒有使用(到目前爲止)。

4.使用英文單詞包來生成文本,而不是使用字符串「Hello World」。

提示:「Pascal case」(也稱爲「上駱駝案例」)意味着字符串中的每一個單詞(包括第一個單詞)都以大寫字母開頭。 因此,「uppercamelcase」變成「UpperCamelCase」。

進行如下更改,以下所示:

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) {
    final wordPair = new WordPair.random();
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          //child: new Text('Hello World'), // Replace the highlighted text...
          child: new Text(wordPair.asPascalCase),  // With this highlighted text.
        ),
      ),
    );
  }
}

5.若是應用程序正在運行,請使用熱從新加載按鈕(閃電圖標)更新正在運行的應用程序。 每次單擊熱從新加載或保存項目時,都會在正在運行的應用程序中隨機選擇不一樣的單詞對。 這是由於配對這個詞是在構建方法內部生成的,每次MaterialApp須要渲染時或者在Flutter Inspector中切換平臺時都會運行。

問題?

若是您的應用程序運行不正常,請查找錯別字。 若是須要,請使用如下連接中的代碼從新開始正軌。

第3步:添加一個有狀態的小部件

無狀態小部件是不可變的,這意味着它們的屬性不能改變 - 全部的值都是最終的。

有狀態的小部件保持在小部件的生命週期中可能改變的狀態。 實現一個有狀態的小部件至少須要兩個類:1)一個StatefulWidget類,它建立一個2)一個State類的實例。 StatefulWidget類自己是不可變的,但State類在整個構件的生命週期中保持不變。

在這一步中,您將添加一個有狀態的小部件RandomWords,它建立其狀態類RandomWordsState。 State類將最終維護小部件的建議和最喜歡的單詞對。

1.將有狀態的RandomWords小部件添加到main.dart。 它能夠在MyApp以外的文件中的任何位置使用,但解決方案將它放在文件的底部。 RandomWords小部件除了建立State類以外幾乎沒有其餘任何東西:

class RandomWords extends StatefulWidget {
  @override
  createState() => new RandomWordsState();
}

2.添加RandomWordsState類。 該應用的大部分代碼都駐留在該類中,該類保持RandomWords小部件的狀態。 這個類將保存隨着用戶滾動而無限增加的生成的單詞對,以及最喜歡的單詞對,由於用戶經過切換心臟圖標來將它們從列表中添加或刪除。

你會一點一點地創建這個類。 首先,經過添加突出顯示的文本建立一個最小類:

class RandomWordsState extends State<RandomWords> {
}

3.在添加狀態類後,IDE會抱怨該類缺乏構建方法。 接下來,您將添加一個基本構建方法,該方法經過將單詞生成代碼從MyApp移動到RandomWordsState來生成單詞對。

將構建方法添加到RandomWordState中,如突出顯示的文本所示:

class RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = new WordPair.random();
    return new Text(wordPair.asPascalCase);
  }
}

4.經過下面突出顯示的更改從MyApp中刪除單詞生成代碼:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = new WordPair.random();  // Delete this line

    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          //child: new Text(wordPair.asPascalCase), // Change the highlighted text to...
          child: new RandomWords(), // ... this highlighted text
        ),
      ),
    );
  }
}

從新啓動應用程序。 若是您嘗試從新加載熱點,則可能會看到一條警告:

Reloading...
Not all changed program elements ran during view reassembly; consider
restarting.

這多是誤報,但考慮從新啓動以確保您的更改反映在應用的用戶界面中。

應用程序應該像之前同樣運行,每次熱從新加載或保存應用程序時都會顯示一個字對。

問題?

若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。

第4步:建立一個無限滾動ListView

在這一步中,您將展開RandomWordsState以生成並顯示單詞配對列表。 當用戶滾動時,ListView小部件中顯示的列表將無限增加。 ListView的builder工廠構造函數容許您根據須要懶惰地構建列表視圖。

1.將一個_suggestions列表添加到RandomWordsState類,以保存建議的詞對。 該變量如下劃線(_)開頭 - 在前面加上一個帶有下劃線的標識符能夠強化Dart語言的隱私。

此外,添加一個largerFont變量來使字體變大。

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];

  final _biggerFont = const TextStyle(fontSize: 18.0);
  ...
}

2.將一個_buildSuggestions()函數添加到RandomWordsState類。 此方法構建顯示建議詞對的ListView。

ListView類提供了一個構建器屬性itemBuilder,一個指定爲匿名函數的工廠構建器和回調函數。 兩個參數傳遞給函數 - BuildContext和行迭代器,迭代器從0開始,每次調用該函數時遞增,每次建議的單詞配對一次。 該模型容許建議的列表在用戶滾動時無限增加。

添加下面突出顯示的行:

class RandomWordsState extends State<RandomWords> {
  ...
  Widget _buildSuggestions() {
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // The itemBuilder callback is called once per suggested word pairing,
      // and places each suggestion into a ListTile row.
      // For even rows, the function adds a ListTile row for the word pairing.
      // For odd rows, the function adds a Divider widget to visually
      // separate the entries. Note that the divider may be difficult
      // to see on smaller devices.
      itemBuilder: (context, i) {
        // Add a one-pixel-high divider widget before each row in theListView.
        if (i.isOdd) return new Divider();

        // The syntax "i ~/ 2" divides i by 2 and returns an integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings in the ListView,
        // minus the divider widgets.
        final index = i ~/ 2;
        // If you've reached the end of the available word pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

}

3._buildSuggestions函數每一個詞對調用_buildRow一次。 這個函數在ListTile中顯示每一個新對,這容許您在下一步中使行更具吸引力。

向RandomWordsState添加_buildRow函數:

class RandomWordsState extends State<RandomWords> {
  ...

  Widget _buildRow(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}

4.更新RandomWordsState的build 方法以使用_buildSuggestions(),而不是直接調用單詞生成庫。 進行突出顯示的更改:

class RandomWordsState extends State<RandomWords> {
  ...
  @override
  Widget build(BuildContext context) {
    final wordPair = new WordPair.random(); // Delete these two lines.
    return new Text(wordPair.asPascalCase);
    return new Scaffold (
      appBar: new AppBar(
        title: new Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }
  ...
}

5.更新MyApp的build方法。 從MyApp中刪除Scaffold和AppBar實例。 這些將由RandomWordsState管理,這使得用戶在下一步中從一個屏幕導航到另外一個屏幕時,能夠更輕鬆地更改應用欄中的路由名稱。

用下面突出顯示的構建方法替換原始方法:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Startup Name Generator',
      home: new RandomWords(),
    );
  }
}

從新啓動應用程序。 你應該看到一個單詞配對清單。 儘量向下滾動,您將繼續看到新的單詞配對。

問題?

若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。

第5步:添加交互性

在這一步中,您將爲每一行添加可點擊的心臟圖標。 當用戶點擊列表中的條目,切換其「收藏」狀態時,該詞語配對被添加或從一組保存的收藏夾中移除。

1.將一個_saved集添加到RandomWordsState。 這個集合存儲用戶最喜歡的單詞配對。 Set比List更受歡迎,由於正確實施的Set不容許重複輸入。

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];

  final _saved = new Set<WordPair>();

  final _biggerFont = const TextStyle(fontSize: 18.0);
  ...
}

2.在_buildRow函數中,添加alreadySaved檢查以確保單詞配對還沒有添加到收藏夾。

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

3.一樣在_buildRow()中,將心形圖標添加到ListTiles以啓用收藏。 稍後,您將添加與心臟圖標進行交互的功能。

添加下面突出顯示的行:

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,
    ),
  );
}

4.從新啓動應用程序。 你如今應該在每一行看到開放的心,但它們尚未互動。

5.在_buildRow函數中讓心靈可點擊。 若是單詞條目已被添加到收藏夾中,再次點擊它將其從收藏夾中刪除。 小心臟被輕敲時,函數調用setState()來通知框架狀態已經改變。

添加突出顯示的行:

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);
        }
      });
    },
  );
}

提示:在Flutter的反應風格框架中,調用setState()會觸發對State對象的build()方法的調用,從而致使UI的更新。

熱從新加載應用程序。 你應該可以點擊任何一行以得到最喜歡的,或不適合的入口。 請注意,點擊一行會生成從心臟圖標發出的隱式墨跡飛濺動畫。

問題?

若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。

第6步:導航到新的屏幕

在這一步中,您將添加一個顯示收藏夾的新屏幕(在Flutter中稱爲路由)。 您將學習如何在主路由和新路由之間導航。

在Flutter中,導航器管理包含應用程序路由的堆棧。 將路由推入導航器的堆棧,將顯示更新爲該路由。 從導航器的堆棧中彈出路由,將顯示返回到前一個路由。

1.向RandomWordsState的構建方法中的AppBar添加列表圖標。 當用戶點擊列表圖標時,包含收藏夾項目的新路線被推送到導航器,顯示該圖標。

提示:某些小部件屬性採用單個小部件(子級),而其餘屬性(如操做)則採用小部件(子級)數組,如方括號([])所示。

將該圖標及其相應的操做添加到構建方法中:

class RandomWordsState extends State<RandomWords> {
  ...
  @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(),
    );
  }
  ...
}

2.將一個_pushSaved()函數添加到RandomWordsState類。

class RandomWordsState extends State<RandomWords> {
  ...
  void _pushSaved() {
  }
}

熱從新加載應用程序。 列表圖標出如今應用程序欄中。 點擊它什麼也沒作,由於_pushSaved函數是空的。

3.當用戶點擊應用欄中的列表圖標時,創建一條路由並將其推送到導航器的堆棧。 此操做會更改屏幕以顯示新路由。

新頁面的內容是使用匿名函數在MaterialPageRoute的builder屬性中構建的。

將呼叫添加到Navigator.push,如突出顯示的代碼所示,將路由推送到導航器的堆棧。

void _pushSaved() {
  Navigator.of(context).push(
  );
}

4.添加MaterialPageRoute及其構建器。 如今,添加生成ListTile行的代碼。 ListTile的divideTiles()方法在每一個ListTile之間添加水平間距。 變量divided保存最後的行,經過便利函數toList()轉換爲列表。

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

5.builder屬性返回一個Scaffold,其中包含名爲「Saved Suggestions」的新路由的應用欄。新路由的主體由包含ListTiles行的ListView組成; 每行由一個分隔符分隔。

添加下面突出顯示的代碼:

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),
        );
      },
    ),
  );
}

6.熱從新加載應用程序。 最喜歡的一些選擇,並點擊應用欄中的列表圖標。 新路線顯示包含收藏夾。 請注意,導航器會在應用欄中添加一個「返回」按鈕。 你沒必要顯式實現Navigator.pop。 點擊後退按鈕返回到主頁路由。

問題?

若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。

第7步:使用主題更改UI

在最後一步中,您將使用該應用的主題。 主題控制你的應用的外觀和感受。 您可使用默認主題,該主題取決於物理設備或模擬器,也能夠自定義主題以反映品牌。

1.您能夠經過配置ThemeData類輕鬆更改應用程序的主題。 您的應用程序目前使用默認主題,但您將更改主要顏色爲白色。

將突出顯示的代碼添加到MyApp,將應用程序的主題更改成白色:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Startup Name Generator',
      theme: new ThemeData(
        primaryColor: Colors.white,
      ),
      home: new RandomWords(),
    );
  }
}

2.熱從新加載應用程序。 請注意,整個背景是白色的,甚至是應用欄。

3.做爲讀者的練習,使用ThemeData來改變UI的其餘方面。 材質庫中的Colors類提供了許多可使用的顏色常量,而熱重載使得用戶界面的實驗變得快速而簡單。

問題?

若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。

完成!

您已經編寫了一個在iOS和Android上運行的交互式Flutter應用程序。 在這個codelab中,你有:

  • 從頭開始建立一個Flutter應用程序。
  • 書寫Dart代碼。
  • 利用外部的第三方庫。
  • 使用熱重載加快開發週期。
  • 實現一個有狀態的小部件,爲你的應用增長交互性。
  • 用ListView和ListTiles建立一個延遲加載的無限滾動列表。
  • 建立了一條路由並添加了在主路由和新路由之間移動的邏輯。
  • 瞭解如何使用主題更改應用UI的外觀。
相關文章
相關標籤/搜索