精讲Flutter官网的第一个例子

栏目: ASP.NET · 发布时间: 5年前

内容简介:学习Flutter你一定会看到官网的第一个例子:中文版 或英文版。但是作为新手,或许你看的会很费劲,这篇文章的目的是帮助你更好的理解这个例子。最终的效果图:我们先分析一下如何实现上图中的效果:

学习Flutter你一定会看到官网的第一个例子:中文版 或英文版。但是作为新手,或许你看的会很费劲,这篇文章的目的是帮助你更好的理解这个例子。

最终的效果图:

精讲Flutter官网的第一个例子

我们先分析一下如何实现上图中的效果:

Android开发者

1. 准备数据:列表数据和选中的数据可以分别使用两个List或者数组存储。 2. 界面列表:使用ListView或RecyclerView 3. 界面跳转:可以使用Intent携带数据到新的列表页

iOS开发者

1. 准备数据:列表数据和选中的数据可以分别使用两个数组存储。 2. 界面列表:使用TableView或CollectionView 3. 界面跳转:使用NavigationController,可以把值直接赋值给新的页面对象

结论

我们发现,无论是原生的Android还是iOS开发,都需要做的步骤是:

  1. 存储要展示的数据,存储选中的数据
  2. 展示列表,并把数据展示出来
  3. 设置跳转到新页面

所以在Flutter开发中,也遵照这几个步骤会更好的理解

Flutter开发

* 准备数据:列表数据使用数组存储,选中的数据可以使用Set存储(因为set可以自动去重)。 * 界面列表:使用ListView * 界面跳转:可以使用Navigator

拆解分析官方代码,带你快速理解

官网上使用大概110行代码实现上面的例子,我们把这些代码拆解成主要的 三部分 来帮助我们学习:

前提:你首先应该会用Android studio或者其他开发 工具 创建一个Flutter的工程,如果你需要学习关于这个步骤,可以在这里快速学习

当你创建一个全新的Flutter工程并运行,界面上会出现熟悉的“Hello world”。 为了更容易的理解Flutter的代码,我们先分析一下创建初始的代码,至少要知道我们需要从哪里开始动手:

精讲Flutter官网的第一个例子

我们要编辑的就是这里的 main.dart 文件,跟其他语言一样,Flutter的入口函数是main函数:

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中单行函数的简写,等价于:
void main() {
  runApp(new MyApp());
} 
复制代码
  1. StatelessWidget 代表只有一种状态的组件,与之对应的是StatefulWidget(表示可能有多种状态)。这里先不用深究其原理,只需知道这个跟flutter的刷新等相关。

  2. 在Widget组件中都是通过build方法来描述自己的内部结构。这里的build表示构建MyApp中使用的是MaterialApp的系统组件。

  3. home标签的值:Scaffold是Material library 中提供的一个组件,我们可以在里面设置导航栏、标题和包含主屏幕widget树的body属性。可以看到这里是在页面上添加了AppBar和一个Text。

  4. Center是一个可以把子组件放在中心的组件

开始改造

我们的目标是把页面中显示hello_world的TextView换成一个ListView。由上面的分析可知,将上面第4点的home标签的值,换成一个ListView就能改变页面显示的内容。不过在此之前,需要先准备一下要显示的数据,这里是使用一个叫english_words 的三方包,可以帮助我们生成显示的单词数据。先学习一下如何添加依赖包:

精讲Flutter官网的第一个例子
  1. 打开pubspec.yaml文件添加三方库:
dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  english_words: ^3.1.0
复制代码
  1. 点击 Packages get 获取刚添加的包。

添加english_words库之后,可以这样使用这个库创造数据:

//创造5个随机词组,并返回词组的迭代器
generateWordPairs().take(5)
复制代码

学习使用可变状态的组件 StatefulWidget

查看ListView的源码,发现其最终是继承自 StatelessWidget,所以它的状态是唯一的。但是要实现的ListView中的数据是动态变化的,所以需要使用StatefulWidget来动态改变ListView中的数据。

使用StatefulWidget组件需要自己控制在不同情况下的显示状态,所以需要实现State类来告诉StatefulWidget类不同情况下如何展示。

创建一个动态变化的组件类,用于表示要显示的ListView:

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

分析:

  1. 创建State类的方法,这里继承系统的State创造一个名叫RandomWordsState的类,来控制ListView各个状态下的展示。
class RandomWordsState extends State<RandomWords> {
}
复制代码

要完成展示数据和保存点击后的数据,这里分别用数组和set来存储(用set存储点击后的数据是因为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. 表示字体大小的常量

创造数据集和构建ListView

添加如下两个方法到RandomWordsState类中,表示创造数据集和构建ListView:

Widget _buildSuggestions() {   
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中
      // 在偶数行,该函数会为单词对添加一个ListTile row.
      // 在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。
      // 注意,在小屏幕上,分割线看起来可能比较吃力。
      itemBuilder: (context, i) {          
        // 在每一列之前,添加一个1像素高的分隔线widget
        if (i.isOdd) return new Divider();

        // 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5
        // 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量
        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);
          }
        });
      },
    );
  }
复制代码

代码中有详细的注释,但是为了方便理解,这里还是给出一点解释:

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

_buildSuggestions方法中:

_suggestions.addAll(generateWordPairs().take(10));

_buildRow方法中:

  • 设置了一行(ListTile代表一行内容,在iOS和android中叫cell)如何展示
  • ListTile设置了title、trailing(右边的图标)和点击事件onTap()
  • 会根据有没有被保存过,决定右边显示什么图标
  • 当点击时,会把没有点击过的内容保存到_saved容器中。

添加跳转逻辑

添加_pushSaved方法表示如何跳转到新的页面并展示选中的数据:

main.dart

void _pushSaved() {
    Navigator.of(context).push(  // 分析 1
      new MaterialPageRoute(  // 分析 2
        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(  // 分析 3
            appBar: new AppBar(
              title: new Text('Saved Suggestions'),
            ),
            body: new ListView(children: divided),
          );
        },
      ),
    );
  }
复制代码

分析: 1.使用Navigator.of(context).push的方式来处理跳转,需要的参数是一个Route 2.创建页面Route 3.返回一个新的里面,里面的body内容是一个ListView,展示的是_saved中读取出来的数据

最终,所有代码整合是如下的样子:

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

复制代码

运行吧,就能看到最上方的效果。

谢谢观看这篇文章,如果让您发现了错误或者有好的建议,欢迎在下方评论给我留言。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Distributed Systems

Distributed Systems

Sukumar Ghosh / Chapman and Hall/CRC / 2014-7-14 / USD 119.95

Distributed Systems: An Algorithmic Approach, Second Edition provides a balanced and straightforward treatment of the underlying theory and practical applications of distributed computing. As in the p......一起来看看 《Distributed Systems》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换