本文是官方文档学习过程的笔记

官方文档: https://flutter.dev/docs/get-started/codelab

本文目的

官方的文档把每个步骤都拆成了小片段, 需要上下文联系的阅读, 如果你和我一样, 入门的时候有阅读理解困难的症状, 本文或许能帮到你, 因为本文的每个代码片段都是基于上一段代码的完整扩展, 可以独立运行, 并且在代码中保留了关键的注释.

初始化项目

# bash
flutter create myapp
cd myapp
flutter run -d <deviceID>

Hello World

编写lib/main.dart, 然后在终端按“r”, 刷新app

import "package:flutter/material.dart";

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

// StatelessWidget 组件是内部没有可变状态的组件,相当于React-Redux的展示组件
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Welcom to Flutter",
      // new Scaffold(), 页面构造器,返回一个拥有appBar,和body的组件
      // 有点类似iOS的 ViewController
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text("Welcom To Flutter"),
        ),
        body: new Center(
          child: new Text("hello world"),
        ),
      )
    );
  }
}

我们会得到下面这个页面

Hello world

使用/添加第三方依赖

在pubspec.yaml中加入english_words依赖:

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  english_words: ^3.1.0

执行命令下载安装依赖(需要科学上网):

# bash
flutter packages get

修改main.dart:

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) {
    // 随机生成一个wordPair文字
    final wordPair = new WordPair.random();
    return new MaterialApp(
      title: "Welcom to Flutter",
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text("Welcom To Flutter"),
        ),
        body: new Center(
          // 使用wordPair生成的文字
          child: new Text(wordPair.asPascalCase),
        ),
      )
    );
  }
}

此时,每次刷新app,都会得到不同的文字, 这一段主要是讲解了如何引用和使用第三方包

创建ListView

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

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

class MyApp extends StatelessWidget {
  // build方法相当于React的render函数
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Welcom to Flutter",
      home: new RandomWords(),
    );
  }
}

// 这里使用了StatefulWidget组件,相当于React-Redux的容器组件
// 组件内部有状态可以维护
class RandomWords extends StatefulWidget{
  @override
    State<StatefulWidget> createState() {
      return new RandomWordsState();
    }
}

class RandomWordsState extends State<RandomWords> {
  final _words = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  Widget _buildWords(){
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      itemBuilder: (context,i){
        // 如果是奇数就返回一条分割线
        if (i.isOdd) return new Divider();
        // i ~/ 2 向下取整
        final index = i ~/ 2 ;
        if(index >= _words.length) {
          // 往_words数组里添加10个字符对象
          _words.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_words[index]);
      },
    );
  }
  Widget _buildRow(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

  // State组件内部也需要重载build方法,build方法相当于React的render函数
  @override
  Widget build(BuildContext context){
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Startup Name Generator"),
      ),
      body: _buildWords(),
    );
  }
}

添加点击事件

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: "Welcom to Flutter",
      home: new RandomWords(),
    );
  }
}

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

class RandomWordsState extends State<RandomWords> {
  final _words = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  final _saved = new Set<WordPair>();
  Widget _buildWords() {
    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 >= _words.length) {
          _words.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_words[index]);
      },
    );
  }

  Widget _buildRow(WordPair pair) {
    // 校验 _saved 是否包含pair对象
    final isSaved = _saved.contains(pair);
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      // cell 尾部组件, 设置为一个Icon
      trailing: new Icon(
        isSaved ? Icons.favorite : Icons.favorite_border,
        color: isSaved ? Colors.red : null,
      ),
      // 当点击cell时,移除或添加当前行的title进_saved
      onTap: () {
        setState(() {
          if (isSaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Startup Name Generator"),
      ),
      body: _buildWords(),
    );
  }
}

如图,可以渲染了列表,并且点击cell,表尾部的Icon会切换颜色

页面跳转

给appBar添加一个actions,并且创建一个IconButton,点击使用Navigator推进一个Scaffold组件

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: "Welcom to Flutter",
      home: new RandomWords(),
    );
  }
}

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

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

  Widget _buildWords() {
    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 >= _words.length) {
          _words.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_words[index]);
      },
    );
  }

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

  @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: () {
                // 实现页面跳转的函数
                Navigator
                    .of(context)
                    .push(new MaterialPageRoute(builder: (context) {
                  return new SavedPage(saved: _saved, biggerFont: _biggerFont);
                }));
              },
            )
          ]),
      body: _buildWords(),
    );
  }
}

// 实现已保存数据的页面
class SavedPage extends StatelessWidget {
  // 声明类的参数
  SavedPage({@required this.saved, @required this.biggerFont});
  final Set<WordPair> saved;
  final TextStyle biggerFont;

  // 渲染一个列表
  @override
  Widget build(BuildContext context) {
    final divided = ListTile
        .divideTiles(
          context: context,
          tiles: saved.map((pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase,
                style: biggerFont,
              ),
            );
          }),
        )
        .toList();
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Saved Words"),
      ),
      body: new ListView(children: divided),
    );
  }
}

如下图Gif, 页面已经可以导航了

修改主题

MaterialApp里面有theme属性,可以设置全局主题

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: "Welcom to Flutter",
      // 这里设置主题
      theme: new ThemeData(
        primaryColor: Colors.green,
        dividerColor: Colors.greenAccent,
      ),
      home: new RandomWords(),
    );
  }
}

如图,替换了主色和分割线的颜色

green主题

您已经编写了一个可在iOS和Android上运行的交互式Flutter应用程序。在这个codelab中,你应该已学到了:

  • 从头开始创建一个Flutter应用程序。
  • 书面Dart代码。
  • 利用外部的第三方库。
  • 使用热重载加快开发周期。
  • 实现一个有状态的小部件,为你的应用增加交互性。
  • 用ListView和ListTiles创建一个延迟加载的无限滚动列表。
  • 实现了页面跳转
  • 了解如何使用主题更改应用UI的外观。

至此,flutter官方快速入门文档已结束

参考资料: https://flutter.io/get-started/codelab/