Flutter学习之入门和体验

1、前言

1.什么是Flutter

上周个人一位微信好友问我有没有学Flutter,我回答说还没真正学,他说应该要接触一下。对于新技术的诞生,我始终保持敬畏之心,和另外一位大学舍友聊了当时如何入坑Android的经历,才发现本身的学习方式和路线有不少的问题,知识点很零乱,知识没有系统化,很少说了,后面学习新的知识必定要从“碎片化”到“总体化”。2018年2月,在世界移动大会上,Google发布了Flutter的第一个beta版本,2018年6月11日发布首个预览版,在2018年12月05日北京时间凌晨1点45分,在Flutter Live上,谷歌Flutter团队推出Flutter1.0,Flutter1.0版本是UI工具包的第一个稳定版本,在2019年2月27日世界移动通讯大会上Google推出1.2版本,带来全新的Web开发工具。另外今日头条团队即将开源让Flutter真正支持View级别的混合开发(上层Flutter Framework引入Widget/LayerTree等概念本身实现了界面描述框架,下层Flutter Engine把LayerTree用OpenGL渲染成用户界面),闲鱼团队也开源了基于 Redux数据管理的组装式Flutter应用框架。什么是Flutter呢?Flutter是一个跨平台的免费开源的移动UI框架,是Google的移动应用SDK,用于在极短期内在iOS和Android平台上建立高质量的原生体验,简而言之就是在iOS下和Android下共用一套代码,一套代码就能在两个操做系统下运行,其官方编程语言是是Dart,学习这门语言很快就上手。Flutter提供不少丰富的UI组件库,开发者能够快速开发出灵活的UI界面,另外Flutter已经加入Material Design组件你们庭中,也就是说Flutter可使用Material Theming和Material 组件,能够相信将来会有更多的创意设计UI风格会涌现。java

2.Flutter的特性

  1. 快速开发,Flutter的热重载可帮助快速轻松试验,构建UI,添加功能和快速修复错误,在iOS和Android的模拟器和硬件上体验亚秒级重载同时不会丢失状态,这里直接引用官方的图:
    热重载图
  2. 机具表现力和美观UI,Flutter内置的Material Design和Cupertino(iOS风格)的部件、丰富的手势API、天然平滑的滑动和不一样的平台表现来提高用户体验,直接看下图:
    Flutter美观UI图
  3. 现代化响应式框架,使用现代化响应式框架和丰富的平台、布局和基础组件来构建用户界面,使用功能强大且灵活的API(针对2D,动画,手势,动效等)解决复杂的用户界面设计。
class CounterState extends State<Counter> {
  int counter = 0;

  void increment() {
    // Tells the Flutter framework that state has changed,
    // so the framework can run build() and update the display.
    setState(() {
      counter++;
    });
  }

  Widget build(BuildContext context) {
    // This method is rerun every time setState is called.
    // The Flutter framework has been optimized to make rerunning
    // build methods fast, so that you can just rebuild anything that
    // needs updating rather than having to individually change
    // instances of widgets.
    return new Row(
      children: <Widget>[
        new RaisedButton(
          onPressed: increment,
          child: new Text('Increment'),
        ),
        new Text('Count: $counter'),
      ],
    );
  }
}
复制代码
  1. 使用平台原生功能及SDK,经过平台API,第三方SDK和原生代码让英语具备强大的扩展性,Flutter容许重复使用现有的Java,Swift和Object代码,并访问iOS和Android上的原生功能和SDK,例以下图获取手机电量数值:
Future<Null> getBatteryLevel() async {
  var batteryLevel = 'unknown';
  try {
    int result = await methodChannel.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level: $result%';
  } on PlatformException {
    batteryLevel = 'Failed to get battery level.';
  }
  setState(() {
    _batteryLevel = batteryLevel;
  });
}
复制代码
  1. 统一应用开发体验:Flutter丰富的工具库可让开发者轻松在iOS和Android设备上实现本身的想法,能够充分利用现有的大部分Java、Object-C或者Swift代码。

3.2019年Flutter产品线

2019年一月底,Flutter团队公布了2019年Flutter的产品线,为如下几点肯定了明确的计划:android

  1. 核心基础:Bug修复,性能调优,内存诊断工具优化,改进测试流程。
  2. 生态系统:更好的C/C++库支持,推动官方开发/维护的Packages达到与核心框架相同质量和完整性。
  3. 动态更新:在国内,这是必不可少的,提供Android上的动态修复:代码更新从服务器推送到Android应用里,也就是热更新。
  4. 支持开发移动端web应用。 而且Flutte UX研究团队会按期根据用户反馈来助力打造更优的Flutter,根据用户反馈来调整开发重点,可见Flutter UX团队的用心和付出程度,今年拭目以待。

2、Flutter架构

1.架构图

Flutter框架从到下包含三部分:函数式响应的Framework(Dart),Engine(C++),Embedder(Platform Specific)。下面直接上图: ios

官方架构图

  • FrameWork是用Dart实现,提供了Material风格的小部件、Cupertino风格的小部件(用于iOS)、文本图像按钮组件、动画、手势等。
  • Engine时用C++实现的,上图详细了,实际包含三部分:Skia、Dart、Text。Skia是一个开源的2D图形库,为硬件和软件平台提供API。Dart包括Dart运行时和垃圾回收。Flutter在调试模式下运行,由JIT(Just in Time,运行时编译,边运行边编译)支持,若是在发布模式下,则是经过AOT(Ahead of Time,运行前编译,普通的静态编译)将Dart代码编译原生的"arm"代码。Text是指文本渲染库。
  • Embedder是指嵌入器,就是可将Flutter嵌入到各类平台中,它的主要任务是渲染Surface设置、线程设置和插件。

2.渲染过程

渲染过程一
渲染过程二
这里结合上面两张图来看,能够知道,当须要更新UI的时候,Framework通知Engine,Engine会等到下个Vsync信号到达的时候,会通知Framework,而后Framework会进行Animate,Build,Layout,Paint,最后生成Layer。Compositor 把全部的Layer组合成Scene,再经过Skia进行处理,最后Engine经过GPU GI接口提交数据给GPU,GPU最后通过处理后在显示器显示。上面简单了解什么Flutter,Flutter特性和框架,有了初步的了解,下面就开始一步一步实现第一个Flutter应用。

3、Flutter环境搭建

1.获取Flutter SDK

务必电脑安装git,本文是在Windows环境下配置的,MAC下配置环境流程是一致的。首先要获取Flutter SDK,使用git去克隆仓库而后添加Flutter工具到本身的环境变量,运行flutter doctor来显示剩下须要安装的依赖,国内用户最好配置一下两个环境变量:git

PUB_HOSTED_URL=https://pub.flutter-io.cn
复制代码

以下图所示: github

配置环境变量一

FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
复制代码

如图所示: web

配置环境变量二
由于此次我第一次在电脑上安装Flutter,因此就要克隆这个远程仓库,我在电脑F盘下执行下面git命令:

$ git clone -b beta https://github.com/flutter/flutter.git
复制代码

如图所示: 编程

git命令下载Flutter SDK
下载比较慢,等了十多分钟才下载完。下载完看到F盘果真有一个名字叫flutter的文件夹,接着将克隆下来的项目bin目录配置到环境变量Path去,由于我是下载到F盘,因此下图路径是:
flutter_sdk路径
若是须要更新flutter的sdk,在命令行执行 flutter upgrade命令便可。

2.运行flutter doctor

打开一个新的命令提示符窗口,运行下面命令flutter doctor,看是否须要安装任何依赖项来完成安装,这个命令会检测环境和在终端生成报告,Dart SDK和Flutter捆绑在一块儿,不必单独去安装Dart。初次运行它会下载本身的依赖库而且自行编译,可能比较慢。后续运行flutter命令就会很快。这里注意,若是CMD窗口显示乱码问题,下面是解决方案:数组

  • Win+R进入进入CMD命令行输入regedit进入注册表
  • 找到 HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe 若是该项下已存在CodePage项,则把值改成十进制”65001”,若是不存在,在该项下新建一个 DWORD(32位值),命名为“CodePage”,值设为“65001”
  • 重启cmd后生效
    flutter中文乱码
    输入flutter doctor运行结果截图所示:
    flutter doctor第一次
    注意看到Android toolchain选项 意思是得要运行flutter doctor --android-licenses赞成协议才能够安装Android工具链。输入flutter doctor --android-licenses一直按y便可。最后从新输入flutter doctor,结果以下图:
    flutter doctor最终结果
    从上面运行结果能够知道一下信息:
  • Flutter的版本和渠道已经安装
  • Flutter运行须要的Android工具链已安装
  • Android studio开发工具已经安装
  • flutter插件和Dart插件没有安装(在Android stdio安装下面会说)
  • 没有链接手机

3.搭建Android Studio开发环境

在Android Studio--File--Settings --plugins搜索Flutter便可,以下图: 服务器

安装Flutter插件
点击安装的时候会弹出一下提示框:
Dart插件安装
大概意思是安装Flutter插件须要Dart插件的支持,就是须要和Dart插件一块儿安装,安装完成后,重启开发工具,就能够新建和开发程序了。 注意:若是提示cannot download xxxx的话去 plugins.jetbrains.com/搜索插件名字,下载对应的插件后解压到对应的位置,以下图:
插件位置
或者点击插件本地安装:
本地安装插件
最后重启本身的编译器便可。

4、建立第一个Flutter程序

1.New Flutter Application

由于如今是要建立应用程序,所以在Create New Flutter Project下选择Flutter Application微信

选择应用程序

2.配置项目信息

在下一个页面输入项目名字,配置Flutter SDK目录,项目存放的路径,项目的描述,公司的域名,项目的包名如图所示:

项目配置
最后一步
最后点击finish等待Android Studio完成所要建立的Flutter项目了。

3.项目目录

项目创建完成后,编辑器给咱们生成的目录以下:

项目目录

  • android:Andorid相关代码目录,里面代码配置和单首创建Andorid项目有些不同
  • ios:iOS相关代码目录,存放Flutter与ios原生交互的一些代码
  • lib:应用源文件,dart文件,核心文件,能够建立不一样的文件夹,存放不一样文件
  • test:测试文件
  • .gitignore:忽略文件,记录一些不须要关注变动记录的文件,就是不添加到版本记录里面
  • .metadata:记录一些Flutter project一些基本信息,如版本,项目类型
  • .packages:记录一些lib文件的路径
  • .iml:是由IntelliJ IDEA建立的模块文件,用于开发Java应用程序的IDE。它存储有关开发模块的信息,该模块多是Java,Plugin,Android或Maven组件; 保存模块路径,依赖关系和其余设置
  • pubspec.lock:这是根据当前项目依赖所生成的文件,记录当前使用的依赖版本
  • pubspec.yaml:包含Flutter应用程序的包数据,这个是配置依赖项的文件,好比配置远程public仓库的依赖项,或者本地资源(图片,音视频)
  • README.md:根据意思就是“读我”,这里会记录一些项目结构,详细信息,帮助信息,了解一个项目都是一般经过这个文件入手。

整体来看和原生Android的工程结构不同了,由于代码都是在lib目录完成的,因此不能用Android多module多lib结构去建立module和lib,除非要用到原生交互的代码,能够在android目录里面去写,而后在lib目录里面去引用。相对Android开发者而言,多了ios目录,ios开发者而言,多了android目录,其余文件上面有具体详细说明。由于lib目录是开发者最主要关注的,打开lib目录,发现有个后缀是dart的文件,里面内容都是用dart语法来写的,还发现有void main() => runApp(MyApp());main()函数对于学过java而言都很是清晰,这个应该是入口函数,另外发现StatelessWidget,StatefulWidget一些小控件,这里想应该是UI组件吧。这里先无论那么多,直接点击运行图标,运行效果图以下:

项目第一次运行效果图
走到这里,说明项目环境配置成功,并成功运行第一个简单的程序。

5、编写第一个应用

1.编写“Hello world”

如今仍是按照新手走,先本身撸个“Hello world”出来吧。把main.dart中全部代码去掉,替换下面代码,屏幕中心显示Hello World

import 'package:flutter/material.dart';

//这个是Dart中单行函数或者方法的简写
void main() => runApp(MyApp());

//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
  // 这个是应用的根widget
  @override
  Widget build(BuildContext context) {
    //注意:一个app只能有一个MaterialApp
    return MaterialApp(
      //标题栏的名字
      title: 'Hello Flutter',
      //这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
      //包含主屏幕的widget树的body属性
      home:new Scaffold(
        appBar:new AppBar(
          title:const Text("Weclome to Flutter"), ), body:const Center( child:const Text("Hello World"), ), ), );
  }
}
复制代码

Android模拟器运行效果以下:

Android模拟器效果
iOS模拟器运行效果以下:
iOS效果运行
感受在iOS运行效果比Android上好多了。看到这里发现Android并非沉浸式状态栏,而iOS默认就是沉浸式了,那有没有办法也能让Android版本也作成沉浸式呢,答案是确定有的,一开始个人思路是应该是在main方法里判断是否是Android版本,若是是就设置,网上找里找,这种方法果真可行:

  1. 首先先导包:
import 'dart:io';
import 'package:flutter/services.dart';
复制代码
  1. 在mian方法根据Android版本作设置:
//入口函数
void main() {
  //MaterialApp组件渲染后
  runApp(MyApp());
  //判断若是是Android版本的话 设置Android状态栏透明沉浸式
  if(Platform.isAndroid){
    //写在组件渲染以后,是为了在渲染后进行设置赋值,覆盖状态栏,写在渲染以前对MaterialApp组件会覆盖这个值。
    SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }
}
复制代码

还有另一种方法,由于Flutter主入口只有一个MainActivity,也就是说全部的Flutter页面都会运行这个MainActivity,那咱们只须要在这个主入口判断一下版本号而后将状态栏颜色设置成透明,具体位置在android->app->src->main->xxx->MainActivity:

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //设置状态栏透明
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
    {//API>21,设置状态栏颜色透明
      getWindow().setStatusBarColor(0);
    }
    GeneratedPluginRegistrant.registerWith(this);
  }
}
复制代码

最终效果Android和iOS下运行以下:

沉浸式统一
这样效果好不少了。另外发现,Android下的标题栏是左边对齐的,那怎么作成iOS那样标题栏在中间呢?很简单,在AppBar加上Center widget就能够了,代码以下:

appBar:new AppBar(
          //ios和android标题栏统一在中间
          title:new Center(child :const Text("Weclome to Flutter")), ), 复制代码

最终效果以下:

ios和Android统一
这样真正作到了iOS和Android版本统一了。上面代码app继承了StatelessWidget,这样自己也成为了widget,在Flutter中, 不少时候一切都看做是widget,layout和padding等等。上面例子结构很清晰明了,就是StatelessWidget类包含了(应用栏)Appbar,和构成主页面widget树结构的body属性。而body的widget又包含了一个Center widget,Center widget又包含一个Text子widget,Center widget能够将子widget对齐到屏幕中心。

2.使用外部package

如今,经过在pubspec.yaml文件配置依赖项,去依赖一个提供英文单词的包,在pub.dartlang.org/flutter/这个网站能够找到不少开源软件包,能够搜索指定的软件包查看对应版本。

开源软件包

  1. pubspec文件管理着Flutter应用程序的静态资源文件,那在pubspec.yaml文件,将english_words(3.1.5版本)添加到依赖项,这里要注意:和^之间是有空格的,不能会出错。以下所示:
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.5
复制代码
  1. 这时候点击编辑器的右上角的Packages get,就是把package拉取到项目中去,而且能够看到控制台输出:Runningflutter packages getin flutter_demo...
    packages获取
    控制台输出
  2. 在lib/main.dart下,将english_words导入,显示灰色证实你导入的库没有使用,以下图所示:
    english_words导入
  3. 改用英文单词的package来生成文本,代码改成以下:
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
  // 这个是应用的根widget
  @override
  Widget build(BuildContext context) {
    //随机生成函数
    var wordPair = new WordPair.random();
    return MaterialApp(
      //标题栏的名字
      title: 'Hello Flutter',
      //这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
      //包含主屏幕的widget树的body属性
      home:new Scaffold(
        appBar:new AppBar(
          //ios和android标题栏统一在中间
          title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( //使用随机生成的英文单词 为何不能const呢 由于 内容发生变化 const是用来修饰常量的 child:new Text(wordPair.asPascalCase), ), ), );
  }
}
复制代码

注意的是Center widget 和 Text widget要用new 来建立,由于内容修改了,并非常量了,这时候按保存就能够看到新的单词出如今屏幕中间文本了。

3.添加有状态的widget

上面MyApp是继承StatelessWidget,StatelessWidget是无状态的也是不可控的,意思是其属性是不能改变的,全部的值都是最终的。在平时开发中,不少控件都须要根据特定场景改变自身的状态,那么Flutter有没有提供有状态的widget呢?答案确定是有的,Statefulwidget是有状态的,在其生命周期保持的状态可能会变化,实现一个有状态的widget至少须要两个类:StatefulWidgets类和State类。

  1. 添加RandomWordsWidget类,这个类继承StatefulWidget类,这个类添加到main.dart文件最底下:
//建立有状态的widget
class RandomWordsWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new RandomWordsState();
  }
}
复制代码
  1. 添加RandomWordsState了,这个类会保存RandomWordsWidget的状态,后面会保存一些特殊不一样状态下的词组,而且在里面添加build方法,否则会报错,把生成单词的代码放到这个build类中去,以下图:
//用来保存RandomWords widget的状态
class RandomWordsState extends State<RandomWordsWidget>{
  @override
  Widget build(BuildContext buildContext){
    var wordPair = new WordPair.random();
    return new Text(wordPair.asPascalCase);

  }
}
复制代码
  1. MyApp下改为建立RandomWordsWidget便可:
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
  // 这个是应用的根widget
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //标题栏的名字
      title: 'Hello Flutter',
      //这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
      //包含主屏幕的widget树的body属性
      home:new Scaffold(
        appBar:new AppBar(
          //ios和android标题栏统一在中间
          title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( child:new RandomWordsWidget() ), ), );
  }

复制代码

这时候运行的效果仍是跟以前同样,不过实现方式不同。

4.建立ListView

下面建立一个滑动组件ListView,开发者接触最多应该是这个滑动组件,下面实现当用户滑动列表的时候,ListView会不断增加,不断显示新的单词。

  1. 在RandomWordsState这个类建立一个数组列表,用来保存词组,这个变量如下划线为开头,Dart语言中下划线前缀是表示强制私有
final _normalWords = <WordPair>[];
复制代码
  1. 在RandomWordsState类添加_buildNormalWords方法,用于构建一个ListView,ListView提供了itemBuilder属性,这是一个工厂builder做为匿名函数进行回调,这个函数须要传入两个参数,一个是BuildContext上下文和行迭代器。对于ListView每一行都会执行这个函数调用,这里想一想好像是平时Android开发中ListView建立添加item的方法。
//建立填充单词的ListView
  Widget _buildNormalWorlds() {
    //内容上下16dp
    return new ListView.builder(
        padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
        //每一个单词都会条约一次itemBuilder,而后将单词添加到ListTile中
        itemBuilder: (context, i) {
          //首先建立10条单词
          if (i >= _normalWords.length) {
            //接着再生成10个单词,添加到列表上
            _normalWords.addAll(generateWordPairs().take(10));
          }
          return _buildItem(_normalWords[i]);
        });
  }
复制代码
  1. 由于ListView是须要设置Item项的样式,这里一样在RandomWordsState里添加_buildItem方法,用来加载指定数据,这里指定内容居中显示:
//设置每一个item项的内容和样式
  Widget _buildItem(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        textAlign: TextAlign.center,
      ),
    );
  }
复制代码
  1. 更改RandomWordsState的build方法,增长_buildNormalWords方法的调用,不是直接用单生成库,代方法以下:
Widget build(BuildContext buildContext) {
// var wordPair = new WordPair.random();删掉
// return new Text(wordPair.asPascalCase);删掉
      return new Scaffold(
        appBar: new AppBar(
          title: new Center(child: const Text("Weclome to Flutter")), ), body:_buildNormalWorlds(), );
  }
复制代码
  1. 更改MyApp的build方法,由于标题的设置放在了RandomWordsState里了,因此不须要再额外添加,并将home变成RandomWords widget,代码一会儿简洁不少以下:
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
  // 这个是应用的根widget
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home:new RandomWordsWidget(),
    );
  }
}
复制代码

最终效果如图所示

flutter第一个列表

  1. 下面添加分割线,让UI更美观,这里经过由于计算机里循环开始都是从0开始的,我_buildNormalWorlds方法里判断是奇数项的话就构造出分割线添加到ListView,这里注意分割线也是一项,那么生成单词的时候要稍微处理下,具体代码以下:
//建立填充单词的ListView
  Widget _buildNormalWorlds() {
    //内容上下16dp
    return new ListView.builder(
        padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
        //每一个单词都会条约一次itemBuilder,而后将单词添加到ListTile中
        itemBuilder: (context, i) {
          //添加分割线
          //奇数行,会添加一个分割线的widget,分割上下单词
          //偶数行,就正常构造添加ListTitle row
          //构造一像素的分割线 也能够本身去定义宽高
          //i.isOdd判断是否奇数,奇数行添加分割线
          if(i.isOdd){
            //注意:这里执行后会跳出循环
            return new Divider();
          }

          //这里对2求商,就是计算出ListView中减去分割线后的实际单词数量
          //如 i为 1,2,3,4,5,那么商结果是0,1,1,2,2
          //1 除以 2商是0 2除以2商是1 以此类推
          final int index = i ~/2;

          //首先建立10条单词
          if (index >= _normalWords.length) {
            //接着再生成10个单词,添加到列表上
            _normalWords.addAll(generateWordPairs().take(10));
          }
          return _buildItem(_normalWords[index]);
        });
  }
复制代码

实际效果以下:

添加分割线后的ListView

5.添加交互

  1. 在RandomWordsState里添加一个_collected的Set集合,这个集合用来存放收藏后的单词,用Set的缘由是Set自己的特性不容许元素有重复值:
final Set<WordPair> _collected = new Set<WordPair>();
复制代码
  1. _buildItem方法中添加isCollected来检查单词是否添加到收藏里
final bool isCollected = _collected.contains(pair);
复制代码
  1. _buildItem之后置属性来添加一个❤️图标到ListTiles,matrial包下有默认的❤️图标,这里就设置下颜色就能够,代码以下:
return new ListTile(
      title: new Text(
        pair.asPascalCase,
        textAlign: TextAlign.center,
      ),
      //trailing 是后置图标属性
      trailing: new Icon(
        //material 包下 icons.dart
        isCollected ? Icons.favorite : Icons.favorite_border,
        //图标颜色设置
        color:isCollected ? Colors.red : null,
      ),
    );
复制代码

运行结果后发现❤️型图标添加到每一行最右边处。

  1. 下面增长点击事件,当点击列表时,若是单词没被收藏,那么就添加收藏,并将心形图标改成收藏后的图标,若是单词被收藏了,那么就移除处收藏,并将心形图标变为默认的心形图标,代码以下:
//trailing 是后置图标属性
      trailing: new Icon(
        //material 包下 icons.dart
        isCollected ? Icons.favorite : Icons.favorite_border,
        //图标颜色设置
        color:isCollected ? Colors.red : null,
      ),

      //item的点击事件属性
      onTap:(){
        //状态设置
        setState(() {
          //若是收藏了
          if(isCollected){
            //那就将set集合里移除
            _collected.remove(pair);
          } else {
            //添加
            _collected.add(pair);
          }
        });

      }
复制代码

效果以下:

增长点赞后效果

  1. 下面实现跳转新页面,首先添加一个收藏单词页面,在Flutter中,导航器管理应用程序的路由栈,什么是路由栈呢?路由栈就是用来管理路由(页面)。就是我如今要打开一个新的页面,那么这个新的页面就会压入路由栈中,若是我如今在新的页面,点击返回到上一个页面,那么路由栈会弹出这个新页面路由,将显示返回到前一个页面,这里想一想和Android原生用栈管理页面的方法同样,首先在首页面增长跳转新页面图标,代码以下:
Widget build(BuildContext buildContext) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Center(child: const Text("Weclome to Flutter")), //增长图标和点击事件动做 Icons.list是icon的类型 onPressed添加点击事件 点击会执行_collectWordsPage方法 actions:<Widget>[ new IconButton(icon: const Icon(Icons.list),onPressed: _collectWordsPage), ], ), body:_buildNormalWorlds(), );
  }
复制代码

实际运行后,AppBar导航栏最右边的位置添加了一个图标,这时候点击没有任何反应,由于_collectWordsPage没有任何代码,下面添加跳转到新页面的实现。

  1. 在_collectWordsPage添加Navigator.push,这个简单易懂,就是会把页面入栈
//跳转新页面开始
  void _collectWordsPage(){
       Navigator.of(context).push(
         
         
       );
  }
复制代码
  1. 下面添加MaterialPageRoute和builder,添加构建ListTile行的代码,并添加项与项之间的分割线,最后经过toList来转换。MaterialPageRoute是一种模态路由,能够经过平台自适应来切换屏幕,Android而言,页面推送过渡向上滑动页面,淡入淡出,弹出过渡则是向下滑动页面。IOS而言,页面从右侧滑入,反向弹出,当另外一个页面进入覆盖时,该页面向左移动。
//跳转新页面开始
  void _collectWordsPage() {
    Navigator.of(context).push(
      new MaterialPageRoute<void>(
        builder: (BuildContext context) {
          //传递收藏后的单词
          final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase, //单词
              ),
            );
          },
          );
        },

      ),
    );
  }
复制代码
  1. 建立Scaffold,其中包含标题栏,body由ListView组成,每个Item项之间有一条分割线,运行发现,新的路由会自动添加返回按钮,这是由于Navigator会在应用栏自动添加一个“返回”按钮,不须要调用Navigator.pop,点击返回按钮会返回到主界面,代码以下:
//跳转新页面开始
  void _collectWordsPage() {
    Navigator.of(context).push(
      new MaterialPageRoute<void>(
        builder: (BuildContext context) {
          //传递收藏后的单词
          final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase, //单词
              ),
            );
          },
          );
          //添加分割线开始
          final List<Widget> divided = ListTile.divideTiles(
            tiles: tiles,
            context:context,
          ).toList();
          return new Scaffold(
            appBar: new AppBar(
              title: new Center(child: const Text("Collect Words")), ), body: new ListView(children: divided), );
        },

      ),
    );
  }
复制代码

最终运行效果发现标题并非居中对齐,由于默认的有边距,这时候须要自定义AppBar就能够,代码以下:

return new Scaffold(
            appBar: new AppBar(
              titleSpacing: 0.0,
              //MaterialPageRoute这个自带了返回键 下面的属性设置取消返回键
              automaticallyImplyLeading:false,
              title: new Container(decoration: new BoxDecoration(color: new Color(0x00000000),
              ),
              child:new Stack(
                children: <Widget>[
                  new Container(
                    //左边位置
                    alignment: Alignment.centerLeft,
                    child:new IconButton(
                      //图标样式
                      icon:new Icon(Icons.arrow_back),
                      //点击事件
                      onPressed: (){
                        Navigator.pop(context);
                      }
                    ),
                  ),
                  new Center(child:new Text("Collect Words")),
                ],
              ),)
            ),
            body: new ListView(children: divided),
          );
复制代码

最终运行效果以下,左边是IOS,右边是Android:

最终演示版

6、总结

入门Flutter的第一天,简单知道了一下几点:

  1. Flutter的特性和框架构成,它和大多数构建移动应用的工具不同,它是用本身的渲染引擎来绘制Widget,它中间层只有C/C++代码,Flutter使用Dart语言实现系统的绝大部分功能(布局,动画,手势)。
  2. 开发环境的搭建。
  3. 体验Flutter添加UI的步骤。
  4. 路由(页面)的跳转。

如有错误,欢迎指正~

相关文章
相关标签/搜索