3.编辑 Flutter 应用

本节内容
    1. Flutter 应用程序的基本结构
    1. 查找和使用第三方库
    1. 使用热重载加快开发周期
    1. 添加一个动态改变状态的控件
    1. 如何创建一个无限滑动的 ListView
    1. 添加交互
    1. 界面跳转
    1. 使用主题更改应用程序的外观

1. 创建起始 Flutter 应用程序

1. 删除 lib / main.dart 中的所有代码并替换为如下代码

它在屏幕的中心显示 "Hello World"

//其实是 dart 语言
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'),
        ),
      ),
    );
  }
}

运行程序,可以看到如下效果:

FlutterHelloWorld.png

Material 是一种视觉设计语言,这里使用 Flutter 创建了一个 Material 应用程序。

2. flutter 程序解析
  1. MyApp 继承了使应用程序本身成为小部件的 StatelessWidget,在Flutter中,几乎所有东西都是一个小部件,包括对齐,填充和布局
  2. StatelessWidget 是 immutable(不变的),即所有属性都不可变,是 final 的
  3. Material library 中的 Scaffold 控件提供了提供了默认的 app bar,其中的 body 属性可以包含主屏幕中的控件树
  4. 一个 Widget 提供一个 build() 方法,其工作就是描述如何显示子控件
  5. MyApp 中包含了一个 Center Widget,而 Center Widget 使其子控件树居中对齐

2. 查找和使用第三方库

开源库仓库:https://pub.dartlang.org/flutter/

1. 添加依赖

在 pubspec.yaml 中添加要依赖的第三方库,例如 english_words 3.1.0:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  english_words: ^3.1.0
2. 获取新的库 Packages Get
FlutterPackageGet.png

点击后可看到如下日志:

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0
3. 升级第三方库:Packages upgrade
F:\Flutter\flutter\bin\flutter.bat --no-color packages upgrade
Running "flutter packages upgrade" in flutter_app...
Process finished with exit code 0
4. 导入第三方库

在 lib / main.dart 中,添加 import english_words,如下:

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

AS 会自动提示

5. 使用第三方库 english_words

使用英文单词包来生成文本,而不是使用字符串“Hello World”,代码如下:

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

3. 使用热重载加快开发周期

如果应用程序正在运行,则使用热重新加载按钮(闪电图标)更新正在运行的应用程序。每次单击热重新加载或保存项目时,都会在正在运行的应用程序中随机选择一个不同的单词对。

因为单词对是在 build() 方法中进行的,每次运行 build() 方法都会重新执行

注意:如果应用进程存在但是应用程序不在前台,则|> 运行按钮和 HotLoad 按钮都不会重新启动应用(即点击没反应),需要先手动打开应用才行

4. 添加一个动态改变状态的控件

相对于不变的 Stateless widget,Stateful widgets 是状态可变的
实现 Stateful widgets 需要两个类:StatefulWidget 类和一个 State 类

1. 创建 StatefulWidget 子类

一般将 StatefulWidget 的子类实现放到文件末尾,其内部只创建了一个 State 类

class RandomWords extends StatefulWidget {
  @override
  createState() => new RandomWordsState();
}
2. 创建 State 子类

在 State 子类中保持 RandomWords widget 的状态

class RandomWordsState extends State<RandomWords> {
    @override
    Widget build(BuildContext context) {
        final wordPair = new WordPair.random();
        return new Text(wordPair.asPascalCase);
    }
}
3. 修改 MyApp 中的 body 代码

将之前的 new Text(wordPair.asPascalCase) 替换为 new RandomWords()

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
        ),
      ),
    );
  }
}
4. 运行 Flutter App

5. 如何创建一个无限滑动的 ListView

1. 准备 ListView 的数据

dart 语言中使用 _ 作为前缀来表示私有变量

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];//使用 _suggestions 来存储 lsitView 数据
  final _biggerFont = const TextStyle(fontSize: 18.0); //使用 _biggerFont 来使字体更大
  ...
}
2. 创建无限滑动的 ListView

在 RandomWordsState 类中添加 _buildSuggestions() 方法来显示 ListView
该方法中的 itemBuilder 使用匿名函数,形参是 context 和 position,position 从 0 开始,一旦用户滑动该匿名方法就会被调用,并且 position 不断增加

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

        //  i ~/ 2 表示整除,例如 1, 2, 3, 4, 5 变成 0, 1, 1, 2, 2.
        final index = i ~/ 2;
        // 加载更多
        if (index >= _suggestions.length) {
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }
}
3. 创建 item 布局

在 RandomWordsState 中添加 _buildRow() 方法用于创建 item

class RandomWordsState extends State<RandomWords> {
  ...

  Widget _buildRow(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}
4. 调用创建 listView 的方法
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 中的无用控件
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Startup Name Generator',
      home: new RandomWords(),
    );
  }
}

6. 添加交互

1. 新建 list 集合保存用户喜欢的 item
class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];

  //使用 _saved 集合来保存状态
  final _saved = new Set<WordPair>();

  final _biggerFont = const TextStyle(fontSize: 18.0);
  ...
}
2. 新建成员变量来判断 item 是否添加到了喜欢列表
Widget _buildRow(WordPair pair) {
  final alreadySaved = _saved.contains(pair);
  ...
}
3. 添加喜欢的心型 icon
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. 添加点击事件

在 Flutter 中,调用 setState() 会再次触发 build() 方法,从而更新 UI

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() 方法并传入一个匿名函数
      setState(() {
        if (alreadySaved) {
          _saved.remove(pair);
        } else {
          _saved.add(pair);
        }
      });
    },
  );
}

7. 界面跳转

在 Flutter 中,Navigator 使用栈管理各个界面

1. 添加 Navigation 导航 icon
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(),
    );
  }

  void _pushSaved() {}
  ...
}
2. 点击导航 icon 时新建界面

使用 MaterialPageRoute 新建界面

void _pushSaved() {
  Navigator.of(context).push(
    new MaterialPageRoute(
      builder: (context) {

        // 创建已选中的 item
        final tiles = _saved.map(
          (pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase,
                style: _biggerFont,
              ),
            );
          },
        );

        // divideTiles 为 tiles 添加分割线
        final divided = ListTile
          .divideTiles(
            context: context,
            tiles: tiles,
          )
          .toList();

        //创建 Scaffold 并添加 appBar 和 listView 作为新界面
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('Saved Suggestions'),
          ),
          body: new ListView(children: divided),
        );
      },
    ),
  );
}
3. 运行效果
Navigation运行效果.png

8. 使用主题更改应用程序的外观

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. 运行效果
FlutterThemeResult.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,914评论 25 707
  • 在 上一篇文章 中,我们讲解了 Flutter 开发环境搭建 , 以及运行了官方demo简单体验了下 Flutte...
    Lmaoshammy阅读 2,290评论 6 4
  • 昂~ 一句话,今天心情很好。 如果不是姨妈有点折腾我,这会是个完美的假期。 生活就是这样,在你特别特别甜的时候,还...
    西槿寒阅读 609评论 2 0
  • 9月13日晚上和大家一起观看了军训慰问演出。主题是 青春喜迎十九大 不忘初心跟党走 2017新生军训慰问演出。演出...
    颦眉阅读 152评论 0 0
  • 最近突然开始思考一些事情。 发现自己之前好像一直一直在如一张白纸一样去生活。
    初心啦啦啦阅读 167评论 0 1