flutter分享

移动端开发新趋势Flutter

Flutter是Google开发的新一代跨平台方案,Flutter可以实现写一份代码同时运行在iOS和Android设备上,并且提供很好的性能体验。Flutter使用Dart作为开发语言,这是一门简洁、强类型的编程语言。Flutter对于iOS和Android设备,提供了两套视觉库,可以针对不同的平台有不同的展示效果。

在Google刚推出Flutter时,其发展很缓慢,终于在18年发布第一个Bate版之后迎来了爆发性增长,发布第一个Release版时增长速度更快。可以从Github上Star数据看出来这个增长的过程。在19年最新的Flutter 1.2版本中,已经开放Web支持的Beta版。

flutter分享_第1张图片
目前运用flutter的大型项目:

  • 阿里咸鱼
  • 头条的抖音
  • 腾讯的NOW直播
    整体框架
    Flutter可以理解为开发SDK或者工具包,其通过Dart作为开发语言,并且提供Material和Cupertino两套视觉控件,视图或其他和视图相关的类,都以Widget的形式表现。Flutter有自己的渲染引擎,并不依赖原生平台的渲染。Flutter还包含一个用C++实现的Engine,渲染也是包含在其中的。

flutter分享_第2张图片
hot Reload
Flutter支持亚秒级热重载,Android Studio和VSCode都支持Hot Reload的特性。但需要区分的是,热重载和热更新是不同的两个概念,热重载是在运行调试状态下,将新代码直接更新到执行中的二进制。而热更新是在上线后,通过Runtime或其他方式,改变现有执行逻辑。

跨平台方案对比
flutter分享_第3张图片

类型 React Native flutter
语言 JavaScript(动态语言) dart(伪动态语言的强类型语言)
环境 JSCore Flutter Engine
发布时间 2015 2017
star 78k+ 67k+
对比版本 0.59.9 1.6.3
空项目打包大小 Android 20M(可调整至 7.3M) / IOS 1.6M Android 5.2M / IOS 10.1M
代码产物 JS Bundle 文件 二进制文件
维护者 Facebook Google
风格 响应式,Learn once, write anywhere 响应式,一次编写多平台运行
支持 Android、IOS、(PC) Android、IOS、(Web/PC)
使用代表 京东、携程、腾讯课堂 闲鱼、美团B端
插件安装 npm插件 pub插件

缺点:
在跨平台开发中,就不得不说到接入原有平台的支持,比如 在 Android 平台上接入 x5 浏览器 、接入视频播放框架、接入 Lottie 动画框架等等。
这一需求 React Native 先天就支持,甚至在社区就已经提供了类似 lottie-react-native 的项目。 因为 React Native 整个渲染过程都在原生层中完成,所以接入原有平台控件并不会是难事 ,同时因为发展多年,虽然各类第三方库质量参差不齐,但是数量上的优势还是很明显的。目前为止, Flutter 原生控件的接入上是仍不如 React Native 稳定

flutter在window环境下安装安装

  1. 获取最新的flutter安装包

  2. 更新环境变量
    在“用户变量”下检查是否有名为“Path”的条目:
    如果该条目存在, 追加 flutter\bin的全路径,使用 ; 作为分隔符.
    如果条目不存在, 创建一个新用户变量 Path ,然后将 flutter\bin的全路径作为它的值.

  3. 运行flutter doctor检查一下flutter基本环境,是否还需要安装什么依赖,缺什么根据提示安装什么

  4. 编辑器的一些配置(我自己用的Idea, 直接file->setting->plugin->安装flutter)

如何快速和运行一个flutter项目

  1. 可视化的方式

    新建:
    file -> new -> flutter

    启动:
    点击绿色三角形启动flutter项目

  2. 命令行的方式:

flutter create flutter_app 
运行flutter run

如何调试和使用开发工具

在这里插入图片描述

dart基础

参考资料:

  • dart中文网
  • dart常用语法:

dart常用语法

在Dart中,一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自Object

  • 注释:
//

/*
*
*/

///
  • 控制台输出: print

  • 字符串使用单引号或双引号均可,如’hello’, “hello”
    字符串插入可以使用类似$name${name}的语法

  • Dart中变量可以以字母或下划线开头,后面跟着任意组合的字符或数字

  • 数据类型:
    Dart有如下几种内建的数据类型:

numbers// int double 
strings
booleans
lists(或者是arrays)//List List
maps// Map
runes(UTF-32字符集的字符)
symbols
Function
  • 如何定义变量

你可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。

var a = 1;
  int b = 10;
  String s = "hello";
  dynamic c = 0.5;

注意:没有赋初始值的变量都会有默认值null

  • final和const

如果你绝不想改变一个变量,使用final或const,不要使用var或其他类型,一个被final修饰的变量只能被赋值一次,一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)。可以这么理解:final修饰的变量是不可改变的,而const修饰的表示一个常量。
注意:实例变量可以是final的但不能是const的

 final String name = 'zhangsan';
  name = 'lisi'; // 编译不通过,被final修饰的是常量,不可重新赋值
  const a = 0;
  a = 1; // 错误

final和const的区别:

区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。

区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。

  • 函数
  1. Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。虽然Dart推荐你给函数加上返回值,但是不加返回值的函数同样可以正常工作,另外你还可以用=>代替return语句,
  2. 使用花括号将函数的参数括起来就是定义了命名参数,定义命名参数时,你可以以{type paramName}或者{paramName: type}两种方式声明参数,而调用命名参数时,需要以funcName(paramName: paramValue)的形式调用。
  3. 命名参数的参数并不是必须的,如果要表示一个参数是必须的,@required注解来标识一个命名参数,这代表该参数是必须的,你不传则会报错
  4. 使用中括号[]括起来的参数是函数的位置参数,代表该参数可传可不传,位置参数只能放在函数的参数列表的最后面。
  5. 你可以为命名参数或者位置参数设置默认值
  6. ***不论在Dart还是Flutter中,必须都需要一个顶层的main()函数,它是整个应用的入口函数,main()函数的返回值是void
  7. 所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null
// 声明返回值
int add(int a, int b) {
  return a + b;
}

// =>是return语句的简写
add3(a, b) => a + b; 

sayHello({String name}) {
  print("hello, my name is $name");
}

sayHello2({name: String}) {
  print("hello, my name is $name");
}

const Scrollbar({Key key, @required Widget child})//child是必须的

sayHello(String name, int age, [String hobby]) { // 位置参数可以有多个,比如[String a, int b]
  StringBuffer sb = new StringBuffer();
  sb.write("hello, this is $name and I am $age years old");
  if (hobby != null) {
    sb.write(", my hobby is $hobby");
  }
  print(sb.toString());
}

// 命名参数的默认值
int add({int a, int b = 3}) { // 不能写成:int add({a: int, b: int = 3})
  return a + b;
}

// 位置参数的默认值
int sum(int a, int b, [int c = 3]) {
  return a + b + c;
}

  • 运算符
    Dart中的运算符与Java中的类似,比如++a a == b b ? a : b,但是也有一些与Java不太一样的运算符
main() {
  // 与Java相同的运算符操作

  int a = 1;
  ++a;
  a++;
  var b = 1;
  print(a == b);  // false
  print(a * b); // 3
  bool real = false;
  real ? print('real') : print('not real'); // not real
  print(real && a == b); // false
  print(real || a == 3); // true
  print(a != 2); // true
  print(a <= b); // false
  var c = 9;
  c += 10;
  print("c = $c"); // c = 19
  print(1<<2); // 4

  // 与Java不太一样的运算符操作

  // is运算符用于判断一个变量是不是某个类型的数据
  // is!则是判断变量不是某个类型的数据
  var s = "hello";
  print(s is String); // true
  var num = 6;
  print(num is! String); // true

  // ~/才是取整运算符,如果使用/则是除法运算,不取整
  int k = 1;
  int j = 2;
  print(k / j); // 0.5
  print(k ~/ j); // 0

  // as运算符类似于Java中的cast操作,将一个对象强制类型转换
  (emp as Person).teach();

  // ??=运算符 如果 ??= 运算符前面的变量为null,则赋值,否则不赋值
  var param1 = "hello", param2 = null;
  param1 ??= "world";
  param2 ??= "world";
  print("param1 = $param1"); // param1 = hello
  print("param2 = $param2"); // param2 = world
  
  // ?.运算符
  var str1 = "hello world";
  var str2 = null;
  print(str1?.length); // 11
  print(str2?.length); // null 
  print(str2.length); // 报错
}

class Person {
  eat() {
    print("I am eating...");
  }

  sleep() {
    print("I am sleeping...");
  }

  study() {
    print("I am studying...");
  }
}

main() {
  // 依次打印
  //  I am eating...
  //  I am sleeping...
  //  I am studying...
  //使用..调用某个对象的方法(或者成员变量)时,返回值是这个对象本身
  new Person()..eat()
      ..sleep()
      ..study();
}


  • 控制流程
    if / else switch for /while try / catch语句跟Java中都类似,try / catch语句可能稍有不同
main() {
  // if else语句
  int score = 80;
  if (score < 60) {
    print("so bad!");
  } else if (score >= 60 && score < 80) {
    print("just so so!");
  } else if (score >= 80) {
    print("good job!");
  }

  // switch语句
  String a = "hello";
  // case语句中的数据类型必须是跟switch中的类型一致
  switch (a) {
    case "hello":
      print("haha");
      break;
    case "world":
      print("heihei");
      break;
    default:
      print("WTF");
  }

  // for语句
  List list = ["a", "b", "c"];
  for (int i = 0; i < list.length; i++) {
    print(list[i]);
  }
  for (var i in list) {
    print(i);
  }
  // 这里的箭头函数参数必须用圆括号扩起来
  list.forEach((item) => print(item));

  // while语句
  int start = 1;
  int sum = 0;
  while (start <= 100) {
    sum += start;
    start++;
  }
  print(sum);

  // try catch语句
  try {
    print(1 ~/ 0);
  } catch (e) {
    // IntegerDivisionByZeroException
    print(e);
  }
  try {
    1 ~/ 0;
  } on IntegerDivisionByZeroException { // 捕获指定类型的异常
    print("error"); // 打印出error
  } finally {
    print("over"); // 打印出over
  }
}

类的定义个构造方法:

  1. Dart中的类没有访问控制,所以你不需要用private, protected, public等修饰成员变量或成员函数
  2. 类除了有跟类名相同的构造方法外,还可以添加命名的构造方法
  3. Dart中使用extends关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,必须调用父类命名的构造方法。格式如下这种,super写在花括号外面。
  4. 上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java很类似,你可以为某个类的成员变量提供getter/setter方法(至今没用上过)
class Person {
  String name;
  int age;
  String gender;
  Person(this.name, this.age, this.gender);//
  sayHello() {
    print("hello, this is $name, I am $age years old, I am a $gender");
  }
}

 var p = new Person("zhangsan", 20, "male");
  p.sayHello(); // hello, this is zhangsan, I am 20 years old, I am a male
  p.age = 50;
  p.gender = "female";
  p.sayHello(); // hello, this is zhangsan, I am 50 years old, I am a female

//命名构造
class Point {
  num x, y;
  Point(this.x, this.y);
  // 类的命名构造方法
  Point.origin() {
    x = 0;
    y = 0;
  }
}

main() {
  // 调用Point类的命名构造方法origin()
  var p = new Point.origin();
  var p2 = new Point(1, 2);
}

class Human {
  String name;
  Human.fromJson(Map data) {
    print("Human's fromJson constructor");
  }
}

class Man extends Human {
  Man.fromJson(Map data) : super.fromJson(data) {
    print("Man's fromJson constructor");
  }
}

//getter setter
class Rectangle {
  num left, top, width, height;

  // 构造方法传入left, top, width, height几个参数
  Rectangle(this.left, this.top, this.width, this.height);

  // right, bottom两个成员变量提供getter/setter方法
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

抽象类和抽象方法

abstract修饰一个类,则这个类是抽象类,抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,

abstract class Doer {
  // 抽象方法,没有方法体,需要子类去实现
  void doSomething();
  // 普通的方法
  void greet() {
    print("hello world!");
  }
}

class EffectiveDoer extends Doer {
  // 实现了父类的抽象方法
  void doSomething() {
    print("I'm doing something...");
  }
}

枚举类
使用enum关键字定义一个枚举类

enum Color { red, green, blue }

静态成员变量和静态成员方法

// 类的静态成员变量和静态成员方法
class Cons {
  static const name = "zhangsan";
  static sayHello() {
    print("hello, this is ${Cons.name}");
  }
}

main() {
  Cons.sayHello(); // hello, this is zhangsan
  print(Cons.name); // zhangsan
}

mixins

mixins是一个重复使用类中代码的方式(不是很理解)

class A {
  a() {
    print("A's a()");
  }
}

class B {
  b() {
    print("B's b()");
  }
}

// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;

main() {
  C c = new C();
  c.a(); // A's a()
  c.b(); // B's b()
}
  • dart库导入
  1. 导入第三方库
  2. 导入自己写的代码,使用相对路径即可
  3. 使用as为关键字为导入的某一个包设置前缀,或者别名
  4. 可以再导入包时使用show hide关键词来导入某个包中的部分功能
  5. 导入包时使用deferred as可以让这个包懒加载,懒加载的包只会在该包被使用的时得到加载,而不是一开始就加载
import 'dart:html';

import './util.dart';

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();

// 只导入foo
import 'package:lib1/lib1.dart' show foo;

// 导入除了foo的所有其他部分
import 'package:lib2/lib2.dart' hide foo;

import 'package:greetings/hello.dart' deferred as hello;
  • 异步
    提供了类似es7中async await等异步操作
Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

构建用户常用组件:

  • 布局相关组件:
    推荐相关文章:flutter布局

flutter分享_第4张图片
布局组件:

类型 作用特点
Container 只有一个子 Widget。默认充满,包含了padding、margin、color、宽高、decoration 等配置。
Padding 只有一个子 Widget。只用于设置Padding,常用于嵌套child,给child设置padding。
Center 只有一个子 Widget。只用于居中显示,常用于嵌套child,给child设置居中。
Stack 可以有多个子 Widget。 子Widget堆叠在一起。
Column 可以有多个子 Widget。垂直布局。
Expanded 只有一个子 Widget。在 Column 和 Row 中充满。
Row 可以有多个子 Widget。水平布局。
ListView 可以有多个子 Widget。自己意会吧。
class _flutterLayoutPageState extends State {
  final pageController = new PageController();
  int tabIndex = 0;

  Future handleRefresh() async {
    await Future.delayed(Duration(microseconds: 1));
    return null;
  }

  _item(String title, Color color) {
    return Container(
      alignment: Alignment.center,
      decoration: BoxDecoration(color: color),
      child: Text(title, style: TextStyle(fontSize: 22, color: Colors.white70)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('常用基本布局组件'),
        leading: GestureDetector(
          onTap: () {
            Navigator.pop(context);
          },
          child: Icon(Icons.arrow_back),
        ),
      ),
      body: RefreshIndicator(
        onRefresh: handleRefresh,
        child: ListView(
          children: [
            Container(
              decoration: BoxDecoration(color: Colors.white70),
              child: Column(
                children: [
                  ClipOval(
                    //原型的
                    child: SizedBox(
                      width: 100,
                      height: 100,
                      child: Image.network(
                        "http://www.devio.org/img/avatar.png",
                      ),
                    ),
                  ),
                  ClipOval(
                    //原型的
                    child: Image.network(
                      "http://www.devio.org/img/avatar.png",
                      width: 50,
                      height: 50,
                    ),
                  ),
                  Padding(
                      padding: EdgeInsets.all(10),
                      child: ClipRRect(
                        //设置圆角
                        borderRadius: BorderRadius.all(Radius.circular(10)),
                        child: Opacity(
                          opacity: 0.6,
                          child: Image.network(
                            "http://www.devio.org/img/avatar.png",
                            width: 50,
                            height: 50,
                          ),
                        ),
                      )),
                  Text("雄安生活"),
//                  Image(
//                    image: AssetImage("assets/image/pic_1.jpg"),
//                    width: 50,
//                    height: 50,
//                  ),
                  Image.network(
                    "http://www.devio.org/img/avatar.png",
                    width: 50,
                    height: 50,
                  ),
                  TextField(
                    decoration: InputDecoration(
                        contentPadding: EdgeInsets.fromLTRB(5, 0, 0, 0),
                        hintText: "请输入",
                        hintStyle: TextStyle(fontSize: 15)),
                  ),
                  Container(
                    margin: EdgeInsets.all(10),
                    height: 100,
                    decoration: BoxDecoration(color: Colors.transparent),
                    child: PhysicalModel(
                        //使显示图形据有圆角
                        color: Colors.transparent,
                        borderRadius: BorderRadius.circular(15),
                        clipBehavior: Clip.antiAlias, //抗锯齿
                        child: PageView(
                          children: [
                            _item("雄安生活", Colors.blue),
                            _item("雄安资讯", Colors.deepOrange),
                            _item("个人中心", Colors.amber),
                          ],
                        )),
                  ),
                  Column(
                    children: [
                      FractionallySizedBox(
                        widthFactor: 1,
                        child: Container(
                          //实际上没有撑满,只有内容的宽度
                          margin: EdgeInsets.fromLTRB(0, 0, 0, 10),
                          child: Text("宽度撑满"),
                          decoration: BoxDecoration(color: Colors.blue),
                        ),
                      ),
                      Container(
                        //实际上没有撑满,只有内容的宽度
                        child: Text("宽度撑满2"),
                        decoration: BoxDecoration(color: Colors.blue),
                      ),
                    ],
                  ),
                  Stack(
                    children: [
                      Image.network(
                        "http://www.devio.org/img/avatar.png",
                        width: 100,
                        height: 100,
                      ),
                      Positioned(
                        left: 0,
                        bottom: 0,
                        child: Image.network(
                          "http://www.devio.org/img/avatar.png",
                          width: 36,
                          height: 36,
                        ),
                      )
                    ],
                  ),
                  Wrap(
                    //创建一个wrap布局,从左到右进行排列,会自动换行
                    spacing: 8, //水平间距
                    runSpacing: 6, //垂直间距
                    children: [
                      _chip("弹性公交"),
                      _chip("预约班车"),
                      _chip("公务出行"),
                      _chip("百度无人车"),
                      _chip("共享单车"),
                      _chip("小邮局"),
                    ],
                  ),
//                  Expanded(
//                    //默认container的高度只有内容的高度,使用这个之后可以设置高度填满剩余高度
//                    child: Container(
//                      decoration: BoxDecoration(color: Colors.deepOrange),
//                      child: Text("拉伸填满高度"),
//                    ),
//                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        onTap: (index) {
          print(index);
//          pageController.animateToPage(index, duration: null, curve: null);
          //, Duration(seconds: 1), Curve()
          setState(() {
            tabIndex = index;
          });
        },
        currentIndex: tabIndex,
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.home, color: Colors.grey),
              activeIcon: Icon(Icons.home, color: Colors.blue),
              title: Text("雄安生活")),
          BottomNavigationBarItem(icon: Icon(Icons.note), title: Text("雄安资讯")),
          BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("个人中心"))
        ],
      ),
    );
  }

  _chip(String label) {
    return Chip(
      label: Text(label),
      avatar: CircleAvatar(
        backgroundColor: Colors.blue.shade900,
        child: Text(label.substring(0, 1)),
      ),
    );
  }
}

  • statelessWidget和statefulWidget组件
    flutter分享_第5张图片

statelessWidget无状态组件:

flutter分享_第6张图片
继承自statelessWidget的组件有哪些:
container

  • 如何使用flutter包和网站
    flutter包管理平台
    打开pubspec.yaml
    添加
dependencied:
    flutter_color_plugin: ^0.0.2
    
    运行flutter packages get或者点击packages get
    
    
    引入包
    import "package:flutter_color_plugin/flutter_color_plugin.dart"
    
  • 基本手势:
    手势表示可以从多个单独的指针事件(甚至可能是多个单独的指针)识别的语义动作(例如,轻敲,拖动和缩放)。 完整的一个手势可以分派多个事件,对应于手势的生命周期(例如,拖动开始,拖动更新和拖动结束):
    Tap
    onTapDown 指针已经在特定位置与屏幕接触
    onTapUp 指针停止在特定位置与屏幕接触
    onTap tap事件触发
    onTapCancel 先前指针触发的onTapDown不会在触发tap事件
    双击
    onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
    长按
    onLongPress 指针在相同位置长时间保持与屏幕接触
    垂直拖动
    onVerticalDragStart 指针已经与屏幕接触并可能开始垂直移动
    onVerticalDragUpdate 指针与屏幕接触并已沿垂直方向移动.
    onVerticalDragEnd 先前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动
    水平拖动
    onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
    onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
    onHorizontalDragEnd 先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动
    要从widget层监听手势,请使用 GestureDetector.
    如果您使用的是Material Components,那么大多数widget已经对tap或手势做出了响应,不需要自己监听。
//自己监听
GestureDetector(
                      onTap: (){
                        print("点击");
                        _printString("点击");
                      },
                      onLongPress: (){
                        _printString("长按");
                      },
                      onTapCancel: (){
                        _printString("取消");
                      },
                      onTapDown: (e){
                        _printString("按下");
                      },
                      child: Container(
                        child: Text("老铁走起!"),
                      ),
                    ),
                    
//Material Components
 RaisedButton(
              onPressed: ()=> _openMap(),
              child: Text("打开高德地图"),
            )
  • 如何导入和使用flutter的资源文件:
    图片空间开发详解:
    Image支持如下几种类型的构造函数:
    new Image 用于imageProvider获取图像
    new Image.asset使用key从assetBundle获取的图像
    new Image.network从网络Url获取的图片
    new Image.file从本地文件中获取的图片(这个本地指的是,安卓设备存储地指)
import 'dart:io';
Image.file(File('/sdcard/Download/stack.png'));

//如果提供的是相对路径,利用插件path_provider插件获取相对路径之前的绝对路径
//获取SdCard的路径:
Feture _getLocalFile(String filename)async{
    String dir = (await getExternalStorageDirectory()).path;
    File f = new File('$dir/$filename');
    return f;
}

new Image.memory用于Uint8List获取图像

更具不同的分辨路,获取不同的图片

AssetImage 了解如何将逻辑请求asset映射到最接近当前设备像素比例的asset。为了使这种映射起作用,应该根据特定的目录结构来保存asset

…/image.png
…/Mx/image.png
…/Nx/image.png
…etc.

Image.asset(icons/heart.png, width: 26, height: 26)
//或者 Image(
height: 26,
width: 26,
image: AssetImage(icons/heart.png))

使用自定义Icon:
IconData(
this.codePoint,//必填,对应font Icon对饮的16进制Unicode
this.fontFamily,//字体库体系
this.fontPackage,//字体在哪个包里,不填仅在自己程序包中查找
this.matchTextDirection: false.//图标是否按照图标绘制方向显示
)

fonts:
    - family: devio
      fonts:
        - asset: fonts/devio.ttf



child: new Icon(new IconData(0xf5566, fontFamily: 'devio'),size: 100.0,color: Colors.blue);
  • 如何设置图片的placeholder:
    安装transparent_image插件:
import 'package:transparent_image/transparent_image.dart';
Stack(
          children: [
        Center(
          child: CircularProgressIndicator(),
        ),
        Center(
          child: FadeInImage.memoryNetwork(placeholder: kTransparentImage, image: 'http://www.devio.org/img/avatar.png'),
        ),
          ],
        )

  • 基于http实现网络操纵:
    安装http第三方插件
import 'package:http/http.dart' as http;
...
Future fetchGet() async{
    final response1 = await http.get("http://www.devio.org/io/flutter_app/json/test_common_model.json");
    final result = json.decode(response1.body);//转成Map
    return CommonModal.fromJson(result);
  }
  ....
  class CommonModal{
  final String icon;
  final String title;
  final String url;
  final String statusBarColor;
  final String hideAppBar;

  CommonModal({this.icon, this.title, this.url, this.statusBarColor, this.hideAppBar});

  factory CommonModal.fromJson(Map json1){
    return CommonModal(
        icon: json1['icon'],
        title: json1['title'],
        url: json1['url'],
        statusBarColor: json1['icon'],
        hideAppBar: json1['icon']
    );
  }
}
  1. 异步:Futrue与FutrueBuilder使用技巧:
    Feture:表示再接下得某个时间得值或者错误,借助Feture我们可以再Flutter实现异步操作,类似promise,提供了then 和catchError

有两中状态:
pending-执行中
completed-执行结束,分两中情况,要么成功,要么失败

Feturethen(Feture onValue(T value),{function onError});//第一个参数会成功调用,第二个参数错误时调用,不写第二个参数时,写catchError()

feture.whenCompile: 再异步结束时调用,类似于try-catch后面的finally

feture.timeout():设置异步超时时间

用法:

fetchGet().timeout(Duration(milliseconds: 2000)).then((CommonModal value){
                  setState((){
                    responseString = '请求结果:\nicon:${value.title}\nicon:${value.icon}';
                  });
                }).catchError((err){
                  setState((){
                    print(err);
                    responseString = "报错了";
                  });
                }).whenComplete((){
                  print("end");
                });

FetureBuilder:是一个将异步操作和异步UI更新结合在一块得类,通过它我们可以将网络请求,数据库读取等得结果更新到页面上
里面得属性设置:

1.  feture:Feture对象表示此构造器当前链接得异步计算
2.  initialData: 表示一个非空得Feture完成钱得初始化
3.  builder:AsyncWidgetBuilder类型得回调函数,是一个基于异步交互构建widget得函数
  1. JSON解析与复杂模型转换使用技巧:
    2.1. 第一种自己写模型转换【常用一些简单的】
    2.2. 使用现成的工具:在线Json转换工具
    2.3. 使用第三方插件json_serializable package包。 它是一个自动化的源代码生成器,可以为我们生成JSON序列化模板(自己还没尝试过)
  • 基于shared_preference本地存储操作:
    shared_preferences是Flutter社区开发得一个本地数据存储:简单的额,异步的,持久化的key-value存储系统
    在Android它是基于sharedPreferences的,ios:NSUserDefaults的
final prefs = await SharedPreferences.getInstance();
//设置数据
pres.setInt("count",1);
//读取数据
pres.getInt("count");
//删除数据
prefs.remove("count");
  • 路由的两中方式:
  1. materialApp中定义的routes
ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RouteNavigator(title: 'Flutter Demo Home Page'),
      routes: {
        "less": (BuildContext context) => lessWidget(),
        "full": (BuildContext context) => fullWidget(),
        "layout": (BuildContext context) => flutterLayoutPage(),
        "photo": (BuildContext context) => PhotoApp(),
        "iamge": (BuildContext context) => PhotoPage(),
        "animation": (BuildContext context) => AnimationPage(),
        "animatedWidget": (BuildContext context) => AnimationPageWidget(),
        "animatedBuilder": (BuildContext context) => AnimationPageBuilder(),
        "tabbar": (BuildContext context) => TabbarPage(),
        "http": (BuildContext context) => HttpPage(),
        "fetureBuilder": (BuildContext context) => FetureBuilderPage(),
        "listView": (BuildContext context) => ListViewPage(),
        "gridViewPage": (BuildContext context) => GradeViewPage(),
        "coutpage": (BuildContext context) => CountPage(),
        "webView": (BuildContext context) => WebViewPage(),
      },
    );
  1. navigator跳转
    Navigator是管理route的Widget。Navigator可以通过route入栈和出栈来实现页面之间的跳转
Navigator.push(context, MaterialPageRoute(builder: (context)=>widget));//跳入某个页面
Navigator.pop(context);//跳出某个页面,退出
  • Android如何内嵌flutter:
  1. 配置环境
    为了让Android工程和Flutter工程互不干扰,这里不再以Android工程为工程的跟目录,而是让Android工程和平级的Flutter工程的公共目录作为根目录。 最终的目录结构应该是下面这样的
你的项目根目录(随便什么你喜欢的地方)
          ├── 原生安卓工程(FlutterInAndroid)
          └── Flutter工程 (my_flutter)

所以首先在你的项目根目录下用AS创建一个新的Android原生项目,可以勾选上kotlin支持,这样更舒服。 创建完成后你会得到一个这样的结构

你的项目根目录(随便什么你喜欢的地方)
  └── FlutterInAndroid

FlutterInAndroid目录内是一个完整的Android工程

module模式创建Flutter工程
接下来使用Flutter命令来创建module工程,在你的项目根目录下执行:

flutter create -t module my_flutter

创建完成后你会得到一个这样的结构

你的项目根目录(随便什么你喜欢的地方)
  ├── FlutterInAndroid
  └── my_flutter

my_flutter是一个Flutter的module工程,用来供Android项目引入

在Android工程中引入依赖
在FlutterInAndroid这个Android工程的setting.gradle文件中追加flutter工程的引入
你的项目跟目录/FlutterInAndroid/setting.gradle

include ':app'
//加入下面配置
setBinding(new Binding([gradle: this]))
evaluate(new File(
        settingsDir.parentFile,
        'my_flutter/.android/include_flutter.groovy'
))

在app的build.gradle文件中加入工程依赖
你的项目跟目录/FlutterInAndroid/app/build.gradle

...
dependencies {
    ...
    // 加入下面配置
    implementation project(':flutter')
}
  1. createView
val flutterView = Flutter.createView(this,lifecycle,"route1")//第一个是Activity,第二个参数是一个Lifecycle对象,我们之间取Activity的lifecycle即可,第三个参数是告诉Flutter我们要创建一个什么样的view


。。。
class FlutterActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_flutter)
        val flutterView = Flutter.createView(this@FlutterActivity,lifecycle,"route1")
        val layout = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
        addContentView(flutterView, layout)
    }
}

  1. Fragment
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.activity_main)
        val tx = supportFragmentManager.beginTransaction()
        tx.replace(R.id.content, Flutter.createFragment("route"))
        tx.commit()
    }
  • flutter如何内嵌webView:
    安装插件flutter_webview_plugin
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
...
@override
  Widget build(BuildContext context) {
    return new WebviewScaffold(
      url: "http://ipts.zpmc.com/ids-admin/#/",
      appBar: new AppBar(
        title: const Text('Widget webview'),
        leading: GestureDetector(
          onTap: (){
            Navigator.pop(context);
          },
          child: Icon(Icons.arrow_back),
        ),
      ),
      withZoom: true,
      withLocalStorage: true,
      hidden: true,
      initialChild: Container(
        color: Colors.redAccent,
        child: const Center(
          child: Text('等待中.....'),
        ),
      ),
    );
  }
  ...
  • 如何兼容androidX版本
    手动兼容:
  1. 更新至最新的 distributionUrl
    文件路径:android/gradle/wrapper/gradle-wrapper.properties
    在这里插入图片描述
  2. 更新 build.gradle 中 classpath 与 Kotlin 版本
    文件路径:classpath,此时注意 Kotlin 版本升至 1.3.0 及以上;
    在这里插入图片描述
  3. 添加 androidx 配置
    文件路径:android/gradle.properties

android.useAndroidX=true 代表使用 androidx 库而非 support 库;

android.enableJetifier=true 代表三方包迁移至 androidx
在这里插入图片描述
4. 更新 Module 中 build.gradle SDK 版本
文件路径:android/app/build.gradle,此时注意除了当前 SDK 版本升级之后,androidTestImplementation 也许随着升级;
flutter分享_第7张图片
使用Android studio升级(很多人说,用第二种很可能不成功!)

  • 如何打开第三方app:
    下载第三方包url_launcher
    注意先兼容androidX版本
import 'package:url_launcher/url_launcher.dart';
...
openMap() async{
//    const url = 'geo:52.32,4.917';//APP提供着提供的schema
    const url = 'http://www.baidu.com';//APP提供着提供的schema
    if(await canLaunch(url)){
      await launch(url);
    }else{
      const url = 'http://maps.apple.com/?ll=52.32,4.917';
      if(await canLaunch(url)){
        await launch(url);
      }else{
        throw '无法打开网页${url}';
      }
    }
  }
  ...
  • android内置flutter:
  • Flutter与Native通信:
    Flutter定义了三种类的Channel:
  1. basicMessageChannel: 用于传递字符串和半结构化的信息,持续通信,收到消息后可以回复此次消息。
    Native端:
    BasicMessageChannel(BinaryMessenger messenger, String name, MessageCode codec);
    //BinaryMessenger messenger-消息信使,是消息得发送和接受得工具
    //String name Channel的名字,也是唯一标识符
    //MessageCode codec 消息的编解码器,有几种不同的类型:
        BinaryCodec-最为简单的一中COdec,因为其返回值和入参的类型相同,均为二进制。由于为二进制,在传递内存数据快时不需要再次编解码
        StringCodec-用于字符串与二进制之间的编解码,其编码格式UTF-8
        JSONMessageCodec-用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表,字典。
        StandarMessageCodec-是BasicMessageChannel的默认编解码,其支持基础数据类型,二进制,列表,字典,其工作原理。
    
    void setMessageHandler(BasicMessageChannel.messageHandler handler)
     //让其接收Dart发来的消息,配合BinaryMessage完成消息的处理
     //   BasicMessageChannel.messageHandler原型
     //   public interface MessageHandler{
     //      void onMessage(T var1, BasicMessageChannel.Peply vars)//var1是消息内容,var2是回复此消息的回调函数
     //}
     
     void send(T message)
     void send(T message, BasicMessageChannel.Peply callback)
     //message是要传递给dart的,callback消息发送之后,收到dart回复的回调函数
    

dart端:

const BasicMessageChannel(this.name, this.codec);
//String name:-channel得名字要和Native端保持一致
//MessageCodec codec-消息得解编码器,要和Native端保持一直

setMessageHandler(Future handler(T message))
//Future handler(T message)消息处理器,配合BinaryMessager完成消息得处理。创建好setMessageChannel后,如果要让其接收Native法来得消息,则需要调用setMessageHandler设置一个消息处理器

Future send(T message)
//T message要发送得具体消息
//Future-消息发送出去之后,收到Native回复得回调函数
  1. MethodChannel:用地传递方法调用一次性通信:Flutter调用Native拍照
    Naitve端:
MethodChannel(BinaryMessager messager, String name)
//或
MethodChannel(BinaryMessager messager, String name,MethodCodec codec)
//BinaryMessager messager-消息信使,消息得发送和接收的工具
//String name channel得名字要和dart端保持一致
//MessageCodec codec-消息得解编码器,要和dart端保持一直

setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler)
//@Nullable MethodChannel.MethodCallHandler handler 消息处理器

dart 端:

const MethodChannel(this.name, [this.codec =  const StandardMethodCodec()])
//this.name -channel得名字要和Native端保持一致
//MessageCodec codec-消息得解编码器,默认是 const StandardMethodCodec(),要和Native端保持一直

future invokeMethod(String method,[dynamic arguments])
//String method:要调用Native的方法名
//[dynamic arguments]: 调用Native方法传递的参数,可不传

  1. EventChannel: 用于数据流的通信,持续通信,收到消息后无法回府此次消息,通过常用于Native向dart的通信:手机电量变化,网络连接变化,陀螺仪,传感器
    Native端:
EventChannel(BinaryMessenger messager,String name)
//或
EventChannel(BinaryMessenger messager,String name, MethodCodec codec)
//BinaryMessenger messager-消息信使,是消息的发送和接收的工具
//String name channel得名字要和dart端保持一致
//MethodCodec codec: 消息得解编码器,要和dart端保持一直

void setStreamHandler(EventChannel.StreamHandler handler)
//EventChannel.StreamHandler handler 消息处理器,配合BinaryMessenger完成消息的处理

dart端:

const EventChannel(this.name,[this.codec = const StendardMethodCodec()]);
String name-Channel的名字,要和Native保持一致
MethodCodec codec-消息得解编码器,默认是 const StandardMethodCodec(),要和Native端保持一直

Stream receiveBroadcastStream([dynamic arguments])
[dynamic arguments]-监听事件时向Native传递的数据
  • flutter打包过程:
  1. 创建 keystore
    如果您有现有keystore,请跳至下一步。如果没有,请通过在运行以下命令来创建一个:
    如何生产jks:
    打开Android studio 点击build->Generate Signed ApK -> 选择jks存放地址-> 创建jks -> 完成
    再通过命令将jks转换长keystore:

首先把jks文件转为p12信息文件

keytool -importkeystore -srckeystore F:\pra\flutter_demo\android\app\key.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore F:\pra\flutter_demo\android\app\key.p12

然后把p12文件转为keystore文件


keytool -v -importkeystore -srckeystore F:\pra\flutter_demo\android\app\key.p12 -srcstoretype PKCS12 -destkeystore F:\pra\flutter_demo\android\app\key.keystore -deststoretype JKS
 keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
  1. 引用应用程序中的keystore
    创建一个名为/android/key.properties的文件,其中包含对密钥库的引用:
 storePassword=
     keyPassword=
     keyAlias=key
     storeFile=/key.jks>

  1. gradle中配置签名
    通过编辑/android/app/build.gradle文件为您的应用配置签名

替换:

android {

为:

def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

android {

替换:

buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now, so `flutter run --release` works.
        signingConfig signingConfigs.debug
    }
}

为:

signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}
  1. 使用命令行
    cd ( 为您的工程目录).
    运行flutter build apk (flutter build 默认会包含 --release选项).
    报错了解决办法:更换命令:
flutter build apk --target-platform android-arm64

打包好的发布APK位于\build\app\outputs\apk\release。

未完成的知识点:

  • flutter如何迁移至web中:

  • flutter如何安装Native的插件:

  • State生命周期:
    flutter分享_第8张图片

State的生命周期有四种状态:???没看懂得知识点

created:当State对象被创建时候,State.initState方法会被调用;
initialized:当State对象被创建,但还没有准备构建时,State.didChangeDependencies在这个时候会被调用;
ready:State对象已经准备好了构建,State.dispose没有被调用的时候;
defunct:State.dispose被调用后,State对象不能够被构建。
生命周期图片
完整生命周期如下:

setState

State中比较重要的一个方法是setState,当修改状态时,widget会被更新。比方说点击CheckBox,会出现选中和非选中状态之间的切换,就是通过修改状态来达到的。
查看setState源码,在一些异常的情况下将会抛出异常:

传入的为null;
处在defunct阶段;
created阶段还没有被加载(mounted);
参数返回一个Future对象。

检查完一系列异常后,最后调用代码如下:
_element.markNeedsBuild();
复制代码markNeedsBuild内部,则是通过标记element为diry,在下一帧的时候重建(rebuild)。可以看出setState并不是立即生效,它只是将widget进行了标记,真正的rebuild操作,则是等到下一帧的时候才会去进行。

创建一个State对象时,会调用StatefulWidget.createState;
和一个BuildContext相关联,可以认为被加载了(mounted);
调用initState;
调用didChangeDependencies;
经过上述步骤,State对象被完全的初始化了,调用build;
如果有需要,会调用didUpdateWidget;
如果处在开发模式,热加载会调用reassemble;
如果它的子树(subtree)包含需要被移除的State对象,会调用deactivate;
调用dispose,State对象以后都不会被构建;
当调用了dispose,State对象处于未加载(unmounted),已经被dispose的State对象没有办法被重新加载(remount)。

  • 动画Animation开发指南
    基于tween的动画和基于物理的动画

  • flutter中内嵌Native??
    在Flutter中嵌入Native组件的正确姿势是…

遇到过的坑:

打包问题:
flutter打包的安卓APK安装在高版本的手机上出现闪退
我遇见的闪退问题是因为 我打候用的是flutter build apk 这个命令
可以改成 flutter build apk --target-platform android-arm64
这个命令打包试试

你可能感兴趣的:(flutter)