Flutter 基础

1、 Widget 基本概念

1、flutter中一切皆是Widget,不只是构建页面的UI,还有如布局类、事件处理类、容器类、动画类都是Widget
2、分为 StatefulWidget 和 StatelessWidget 两类,StatefulWidget 是 UI 可以变化的 Widget,创建完后 UI 还可以在更改,StatelessWidget 是 UI 不可以变化的 Widget,创建完后 UI 就不可以在更改(可通过重绘实现更改)
3、MaterialApp 与 Scaffold,MaterialApp 大部分情况下要作为 Flutter 的 根Widget,并且 MaterrialApp 经常和 Scaffold 搭配一起使用。Scaffold 实现了主布局,如 AppBar、Drawer、SnackBar
4、要实现 StatefulWidget,需要实现StatefulWidget类及其State类:

class MyApp extends StatefulWidget{
       String title
       MyApp(this.title)
      @override
      State createState(){
          return  MyAppState('Hello Flutter')
      }
}

class MyAppState extends State{
       @override
        Widget build(BuildContext:context){
             return ...
        }
}

5、StatefulWidget主要作用是创建State,State的主要作用是创建界面以及用 setState 更新界面。
6、setState() 可以刷新UI的原理是,setState() 会触发 StatefulWidget 强制重建,重建的时候会重新创建 Widget 和绑定数据,从而实现了刷新 UI。所以只要 MyApp 是 StatefulWidget,那么它的子类在 setState() 的作用下都可以被强制刷新。
7、State的成员变量:widget,context,mounted
widget 可以访问 StatefulWidget 中的成员变量,如 widget.title
context 是构建上下文,较为复杂
mounted 是 bool 类型,表示当前 State 是否加载到树里,setState() 只有在 mounted 为 true 的时候才能用,当 moundted 为 false 时调用会抛异常

// mounted 一般这么用
if(mounted){
    setState((){
        ...
    })
}

8、有状态组件重建时,重建的部分就是 StatefulWidget,不会重建的部分就是 State
9、StatefulWidget 生命周期,未包含 didChangeAppLifecycleState(监听APP前后台切换)

[图片上传失败...(image-d5d836-1559900244628)]
10、StatelessWidget 生命周期
只有一个build 函数

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  final String content;

  MyApp(this.content);

  @override
  Widget build(BuildContext context) {
    print("build"); //StatelessWidget -- build
    ....
  }
}

11、一个 Flutter App 的 基本结构就是:Root Widget 是 MaterialApp ,然后 MaterialApp 的 子Widget 就是 Scaffold,然后我们在 Scaffolfd 的 子Widget 里写UI
12、MaterialApp常用属性:home(进入程序后显示的第一个页面,必须是 Scaffold),theme(设置 Flutter App 的主题,比如颜色、字体等)
13、Scaffolfd常用属性:appBar(顶部的标题栏,不设置的话就不会显示),backgroundColor(背景颜色),body(要显示的主要内容)

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

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text("Hello World!"),
        ),
      ),
    );
  }
}

2、常用 5 类 Widget

分别是:基础类Widget、布局类Widget、容器类Widget、可滚动类Widget以及手势识别类Widget

2.1、基础类Widget
  • Text/RichText
Text(
    "Hello Flutter",
    style: TextStyle(
        color: Colors.red,
        fontSize: 20.0,
        background: new Paint()..color = Colors.yellow,
        ),
    )

RichText 要传入 TextSpan 数组,每个 TextSpan 是一个独立的文本,可以定义自己的 Style

RichText(
    text: TextSpan(children: [
      TextSpan(text: "Hello", style: TextStyle(color: Colors.blue)),
      TextSpan(text: "Flutter", style: TextStyle(color: Colors.red))
    ]),
  )
  • Image/Icon
    Image.asset 使用本地图片。
    要使用本地图片,需要更改 flutter 配置文件
flutter:
  uses-material-design: true
    
  assets:
    - images/

// 使用时直接
Image.asset("images/flutter.png",fit: BoxFit.cover,)

Image.network 使用网络图片

Image.network("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1557781801455&di=17f9f2fc3ded4efb7214d2d8314e8426&imgtype=0&src=http%3A%2F%2Fimg2.mukewang.com%2F5b4c075b000198c216000586.jpg",fit: BoxFit.cover,)

Image.file 从本地加载图片
常用的配置:fit填充图片,alignment对其图片,repeat重复图片
Icon基本使用

Icon(
  Icons.android,
  size: 50.0,
  color: Colors.green,
)
  • TextField / Form
TextField(
    onChanged: (String data) {
      //实时获取
      print(data);
    },
  )

常用属性:keyboardType弹出什么类型的键盘,如数字、文本等,textInputAction弹出的键盘右下角按钮类型,如 搜索、提交等
Form 使用

import 'package:flutter/material.dart';

void main() => runApp(FormWidget());

class FormWidget extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return FormWidgetState();
  }
}

class FormWidgetState extends State {
  final GlobalKey _formKey = GlobalKey();

  String _userGender = '男';
  String _userName;
  String _userPassword;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primaryColor: Colors.blue,
      ),
      home: Scaffold(
          appBar: AppBar(title: Text("Flutter UI基础Widget -- Form")),
          body: Form(
            key: _formKey,
            child: Column(
              children: [
                DropdownButtonFormField(
                  value: _userGender,
                  items: ['男', '女']
                      .map((label) => DropdownMenuItem(
                            child: Text(label),
                            value: label,
                          ))
                      .toList(),
                  onChanged: (value){
                    setState(() {
                      _userGender = value;
                    });
                  },
                  onSaved: (value){
                    _userGender = value;
                  },
                ),
                TextFormField(
                  decoration: InputDecoration(hintText: '用户名'),
                  validator: (value) { //
                    if (value?.length <= 5) {
                      return '用户名必须大于 5 个字符';
                    }
                  },
                  onSaved: (value) {
                    _userName = value;
                  },
                ),
                TextFormField(
                  decoration: InputDecoration(hintText: '密码'),
                  obscureText: true,
                  validator: (value) {
                    if (value?.length <= 8) {
                      return '密码必须大于 8 个字符';
                    }
                  },
                  onSaved: (value) {
                    _userPassword = value;
                  },
                ),
                RaisedButton(
                  child: Text('注册'),
                  onPressed: () {
                    if (_formKey.currentState.validate()) {
                      _formKey.currentState.save();
                      print(_userGender);
                      print(_userName);
                      print(_userPassword);
                    }
                  },
                )
              ],
            ),
          )),
    );
  }
}
  • SnackBar 轻量级提示组件,需要使用Builder封装,以保证context是Scaffold,而不是MyApp
home: Scaffold(
    appBar: AppBar(title: Text("Flutter UI Widget -- SnackBar 及 Builder")),
    body: Builder(
      builder: (context) => RaisedButton(
        child: Text('Show SnackBar'),
        onPressed: () {
          Scaffold.of(context).showSnackBar(SnackBar(
              content: Text('SnackBar'),
              duration: Duration(seconds: 5)));
        },
      ),
    ),
),
  • 对话框 showAboutDialog() & showDialog(),上下文必须是 MaterialApp
Builder(
    builder: (context) => RaisedButton(
        onPressed: () {
            showAboutDialog(
                context: context,
                applicationName: 'lutter UI Widget -- 对话框',
                applicationVersion: '1.0.0');
            },
        child: Text('RaisedButton'))
    )

// SimpleDialog
                 showDialog(
                    context: context,
                    builder: (context) => SimpleDialog(
                      title: Text('SimpleDialog Demo'),
                      children: [
                        SimpleDialogOption(
                          child: Text('OK'),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        ),
                        SimpleDialogOption(
                          child: Text('CANCEL'),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        )
                      ],
                    ));
// AlertDialog
AlertDialog(
    title: Text('AlertDialog'),
    content: SingleChildScrollView(
      child: ListBody(
        children: [
          Text('This is an alert dialog'),
          Text('add two options.'),
        ],
      ),
    ),
    actions: [
      FlatButton(
        child: Text('Ok'),
        onPressed: () {
          Navigator.of(context).pop();
        },
      ),
      FlatButton(
        child: Text('Cancel'),
        onPressed: () {
          Navigator.of(context).pop();
        },
      )
    ],
  )
// CupertinoAlertDialog 
  • 底部弹出菜单 showBottomSheet & showModalBottomSheet
  • 菜单按钮 PopupMenuButton
PopupMenuButton(
    child: Text('更多'),
    onSelected: (MenuItem result) {
      print('click '+result.toString());
    },
    itemBuilder: (BuildContext context) => >[
      const PopupMenuItem(
        value: MenuItem.menuA,
        child: Text('menu A'),
      ),
      const PopupMenuItem(
        value: MenuItem.menuB,
        child: Text('menu B'),
      ),
      const PopupMenuItem(
        value: MenuItem.menuC,
        child: Text('menu C'),
      ),
      const PopupMenuItem(
        value: MenuItem.menuD,
        child: Text('menu D'),
      ),
    ],
  )
2.2 手势识别 Widget

GestureDetector 是用于检测手势的 Widget

home: Scaffold(
        appBar: AppBar(title: Text("Flutter 手势识别Widget")),
        body: GestureDetector(
          child: Text('手势识别'),
          onTap: (){
            print('点击');
          },
          onDoubleTap: (){
            print('双击');
          },
          onLongPress:  (){
            print('长按');
          },
          onHorizontalDragStart: (DragStartDetails details){
            print('水平滑动');
          },
        ),
      ),
2.3 布局 Widget
  • 布局模型
    Flutter 的布局模型是 BoxConstraint(盒约束)布局模型

1、Tightly Constraints(严格约束)
父级大小固定

body: Container(
    constraints: BoxConstraints.tight(Size(100, 100)), //添加 Tightly Constraints
    color: Colors.red,
    child: Text(
      "HelloWorld",
      style: TextStyle(background: paint),
    ))));
Flutter 基础_第1张图片
严格约束

2、 Loose Constraints(松散约束)
父级大小随子元素,只要子元素不超过父级大小。此外如果父级没有子元素,则父级显示最大大小

body: Container(
        constraints: BoxConstraints.loose(Size(100, 100)), //添加 Loose Constraints
        color: Colors.red,
        child: Text(
          "HelloWorld",
          style: TextStyle(background: paint),
        ))));
松散约束

3、 Bounded Constraints(有界约束)
必须制定四个值,当父级有子元素时,子元素宽度小于最小宽度,父级会以最小宽度展示;无子元素时,父级会以最大宽高显示

body: Container(
    constraints: BoxConstraints(minWidth: 100,maxWidth: 300,minHeight: 0,maxHeight: 300), //添加 Bounded Constraints
    color: Colors.red,)));
有界约束

4、Unbounded Constraints(无界约束)
同理
5、Infinite Constraints(无限约束)
父级永远占满全屏

body: Container(
    constraints: BoxConstraints.expand(), //添加 Infinite Constraints
    color: Colors.red,
    child: Text(
      "HelloWorld",
      style: TextStyle(background: paint),
    ))));
  • 布局 Widget

1、弹性布局 Flex
和CSS中的flex布局非常相似,Flexible 和 Expanded 可以赋予子 Widget 伸缩的能力,当 子Widget 要超过主轴的大小时,会自动换行,当还有剩余空间时,Expanded 会占满剩余的所有空间,而 Flexible 只会占用自身大小的空间。

Flex(
    direction: Axis.horizontal,
    mainAxisAlignment: MainAxisAlignment.start,
    children: [
      Flexible(
        flex: 1,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.yellow,
        ),
      ),
      Flexible(
        flex: 2,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.green,
        ),
      ),
      Flexible(
        flex: 1,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.blue,
        ),
      ),
    ],
  ),
Flutter 基础_第2张图片
flexible
Flex(
    direction: Axis.horizontal,
    mainAxisAlignment: MainAxisAlignment.start,
    children: [
      Expanded(
        flex: 1,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.yellow,
        ),
      ),
      Expanded(
        flex: 2,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.green,
        ),
      ),
      Expanded(
        flex: 1,
        child: Container(
          height: 30.0,
          width: 30.0,
          color: Colors.blue,
        ),
      ),
    ],
  ),
Flutter 基础_第3张图片
Expanded

2、线性布局 Row & Column
Row & Column 都继承自弹性布局 Flex,其实就是确定了主轴方向的 Flex,其余的用法和 Flex 一致。

// Row
Row(
  children: [
    Text("Hello Flutter"),
    Image.asset(
      "images/flutter.png",
      width: 200,
    )
  ],
)

// Column
Column(
  children: [
    Text("Hello Flutter"),
    Image.asset(
      "images/flutter.png",
      width: 200,
    )
  ],
)

3、流式布局 Wrap
页面元素的宽度可以按照屏幕分辨率进行适配调整,但整体布局不变。Wrap 会把超出屏幕显示范围的 Widget 自动换行,所以称为流式布局

body: Wrap(
              direction: Axis.horizontal,
              spacing: 8.0, // 主轴 方向间距
              runSpacing: 12.0, // 交叉轴 方向间距
              alignment: WrapAlignment.center,
              runAlignment: WrapAlignment.start,
              children: [
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('A')),
                  label: new Text('AAAAAAAA'),
                ),
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('M')),
                  label: new Text('BBBBBB'),
                ),
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('H')),
                  label: new Text('CCCCCCCCC'),
                ),
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('J')),
                  label: new Text('DDDDDDDD'),
                ),
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('J')),
                  label: new Text('EEEEEEEE'),
                ),
                new Chip(
                  avatar: new CircleAvatar(
                      backgroundColor: Colors.blue, child: Text('J')),
                  label: new Text('FFFFFFFFFFFFFFFF'),
                ),
              ],
            )));
Flutter 基础_第4张图片
Wrap

4、层叠布局 Stack
层叠布局允许 子Widget 堆叠(按照代码中声明的顺序),同时 子Widget 可以根据到父容器四个边的位置来确定本身的位置

Stack(
  children: [
    Image.asset(
      "images/flutter.png",
       width: 200,
       fit: BoxFit.cover,
    ),
    Text('Hello Flutter',style: TextStyle(fontSize: 50.0),),
  ],
)
Flutter 基础_第5张图片
Stack

为了确定 子Widget 到父容器四个角的位置,Stack 将 子Widget 分为两类: Positioned 和 non-positioned

home: Scaffold(
        appBar: AppBar(title: Text("Flutter布局Widget -- 层叠布局")),
        body: Stack(
          fit: StackFit.expand,
          children: [
            Positioned(
              left: 50,
              top: 100,
              child: Image.asset(
                "images/flutter.png",
                width: 200,
                fit: BoxFit.cover,
              ),
            ),
            Text('Hello Flutter'),
          ],
        ),
      ),
Flutter 基础_第6张图片
Positioned
2.4、容器类 Widget

大部分 UI Widget 都不能指定宽高、设置内边距和外边距,这时候就需要使用 容器类Widget 了
1、Padding

body: Padding(
              padding: EdgeInsets.all(100),
              child: Text('Hello Flutter'),
            )));

2、Align

Align(
  alignment: Alignment.topRight,
  child: Text(
    'Hello Flutter',
    style: TextStyle(color: Colors.red, fontSize: 50),
  ),
 )

3、Center

Center(
  child: Text(
    'Hello Flutter',
    style: TextStyle(color: Colors.red, fontSize: 50),
  ),
)  

4、Container

body: Container(
              margin: EdgeInsets.only(top: 50.0, left: 120.0), //容器外补白
              constraints:
                  BoxConstraints.tightFor(width: 200.0, height: 150.0), //卡片大小
              decoration: BoxDecoration(
                  //背景装饰
                  gradient: RadialGradient(
                      //背景径向渐变
                      colors: [Colors.green, Colors.blue],
                      center: Alignment.topLeft,
                      radius: .98),
                  boxShadow: [
                    //卡片阴影
                    BoxShadow(
                        color: Colors.black54,
                        offset: Offset(2.0, 2.0),
                        blurRadius: 4.0)
                  ]),
              transform: Matrix4.rotationZ(.2), //卡片倾斜变换
              alignment: Alignment.center, //卡片内文字居中
              child: Text(
                //卡片文字
                "Hello Flutter",
                style: TextStyle(color: Colors.white, fontSize: 40.0),
              ),
            )));
2.5、滚动类 Widget

三大类:ListView & GridView & PageView

  • ListView
    与 ListView 相似的还有 SingleChildScrollView & CustomScrollView,从功能性上来看:
    CustomScrollView >ListView >SingleChildScrollView ,大多数时使用ListView,当有特殊交互,如吸顶等时可以使用 CustomScrollView,对于一些设置页,里面有很多设置项,超过一屏时可以使用SingleChildScrollView
// ListView 常见用法
import 'package:flutter/material.dart';

void main() => runApp(ListViewSeparatedWidget(
      items: List.generate(10000, (i) => "Item $i"),
    ));

class ListViewSeparatedWidget extends StatelessWidget {
  final List items;

  ListViewSeparatedWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滚动Widget -- ListView')),
        body: ListView.separated(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('${items[index]}'),
            );
          },
          separatorBuilder: (context, index) {
            return Container(
              constraints: BoxConstraints.tightFor(height: 10),
              color: Colors.orange,
            );
          },
        ),
      ),
    );
  }
}
  • GridView
    GridView 是一个可以构建二维网格列表的 可滚动Widget
import 'package:flutter/material.dart';

void main() => runApp(GridViewBuilderWidget(
      items: List.generate(10000, (i) => "Item $i"),
    ));

class GridViewBuilderWidget extends StatelessWidget {
  final List items;

  GridViewBuilderWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
        body: GridView.builder(
          gridDelegate:
              SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('${items[index]}'),
            );
          },
        ),
      ),
    );
  }
}
Flutter 基础_第7张图片
四列布局
  • PageView
    PageView 是可以一页一页滑动的 可滚动Widet。其 子Widget 会占据当前屏幕的所有可见区域。适合做启动屏的引导页
import 'package:flutter/material.dart';

void main() => runApp(PageViewBuilderWidget(
      items: List.generate(10000, (i) => "Item $i"),
    ));

class PageViewBuilderWidget extends StatelessWidget {
  final List items;

  PageViewBuilderWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滚动Widget -- PageView')),
        body: PageView.builder(
          onPageChanged: (index) {
            print('current page $index ');
          },
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('${items[index]}'),
            );
          },
        ),
      ),
    );
  }
}

3、功能类 Widget

这类Widget 跟布局没什么关系,如 Theme,MediaQuery,Navigator,InheritedWidget

Theme.of(context)  // 返回的类型是 ThemeData ,Theme 的所有数据都存储在 ThemeData 里
MediaQuery.of(context) // 返回的类型是 MediaQueryData,一般关心 Size 和 devicePixelRatio 两个参数
// 路由管理
Navigator.push()
Navigator.pop()
// InheritedWidget 可以高效的将数据在 Widget树 中向下传递,通常用来共享数据

以下主要谈下路由的处理:

  • 页面跳转

1、简单方式直接跳转

Navigator.push(
  context, MaterialPageRoute(builder: (context) => SecondPage()));
Navigator.pop(context);

2、使用路由表跳转

return MaterialApp(
        title: "Flutter Demo",
        theme: ThemeData(
          primaryColor: Colors.blue,
        ),
        initialRoute: '/First',
        routes: {
          '/First': (context) => FirstPage(),
          "/Second": (context) => SecondPage()
        },
        home: FirstPage());

// Navigator.pushNamed 跳转
Navigator.pushNamed(context, '/Second');
Navigator.pop(context);
  • 页面传参
// 方式1
Navigator.push(
   context,
   MaterialPageRoute(
    builder: (context) => SecondPage(),
    settings: RouteSettings(
        arguments:
            PassArgumnets('Data from FirstPage Navigator.push()'))),
);
// 方式2
Navigator.pushNamed(context, '/Second',arguments: PassArgumnets('Data from FirstPage Navigator.pushNamed()'));

// 接受参数
final PassArgumnets passArgumnets =ModalRoute.of(context).settings.arguments;
  • 页面关闭时返回给上级页面参数
Navigator.pop(context,PassArgumnets('Return Data from SecondPage'));

var passArgumnets = await Navigator.pushNamed(context, '/Second',
        arguments: PassArgumnets('Data from FirstPage Navigator.pushNamed()'));
    print(passArgumnets.content);

4、数据请求 & 本地数据读取

  • 数据请求
    可以使用第三方工具库 http
// 添加依赖
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
  http: ^0.12.0+2

// 获取依赖
flutter packages get

// 发get请求
var response = await http.get(
        'https://api.douban.com/v2/movie/in_theaters?apikey=0b2bdeda43b5688921839c8ecb20399b&city=%E6%B7%B1%E5%9C%B3&start=0&count=10');
    //成功获取数据
    if (response.statusCode == 200) {
      print(json.decode(response.body)); // 转成 json 格式
    } 
// 发 post 请求
var client = new http.Client();
try {
  var uriResponse = await client.post('http://example.com/whatsit/create',
      body: {'name': 'doodle', 'color': 'blue'});
  print(await client.get(uriResponse.bodyFields['uri']));
} finally {
  client.close();
}

也可以自己封装一个请求类:

class UserAgentClient extends http.BaseClient {
  final String userAgent;
  final http.Client _inner;

  UserAgentClient(this.userAgent, this._inner);

  Future send(BaseRequest request) {
    request.headers['user-agent'] = userAgent;
    return _inner.send(request);
  }
}

  • 本地数据读取
    使用 shared_preferences 第三方库
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      body: Center(
      child: RaisedButton(
        onPressed: _incrementCounter,
        child: Text('Increment Counter'),
        ),
      ),
    ),
  ));
}

_incrementCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  int counter = (prefs.getInt('counter') ?? 0) + 1;
  print('Pressed $counter times.');
  await prefs.setInt('counter', counter);
}

5、状态管理

  • InheritedWidget

1、创建

class ShareDataInheritedWidget extends InheritedWidget{
  // 共享的数据
  String curCity ;
  // 构造函数
  ShareDataInheritedWidget(this.curCity,{Widget child}):super(child:child);
  // updateShouldNotify 需不需要通知依赖 InheritedWidget 数据的子 Widget
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return (oldWidget as ShareDataInheritedWidget).curCity != curCity;
  }
  //定义一个便捷方法,方便子树中的 Widget 获取 ShareDataInheritedWidget 实例
  static ShareDataInheritedWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataInheritedWidget);
  }
}

2、初始化

class _MyHomePageState extends State {
  ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ShareDataInheritedWidget(
        '深圳',
        child: _widgetItems[_selectedIndex],
      ), //选中不同的选项显示不同的界面,
      ...
    );
  }
  ...
}

3、子 Widget 获取和更新数据

ShareDataInheritedWidget.of(context).curCity;
ShareDataInheritedWidget.of(context).curCity = selectCity;
  • Redux
    1、添加并获取依赖
dependencies:
  ...
  flutter_redux: ^0.5.3

2、创建一个Action

class InitCityAction {
  String city;

  InitCityAction(this.city);
}

3、创建一个 reducer

CityState changeCityReducer(CityState state, dynamic action) {
  if(action is InitCityAction){
    return CityState(action.city);
  }
  return state;
}

4、创建一个 MiddleWare

void readCityFromDisk(
    Store store, dynamic action, NextDispatcher next) async {
  if (action is InitCityAction) {
    String city = await initCity();
    next(InitCityAction(city));
    return;
  }

  next(action);
}

Future initCity() async {
  final prefs = await SharedPreferences.getInstance(); //获取 prefs

  String city = prefs.getString('curCity'); //获取 key 为 curCity 的值

  if (city == null || city.isEmpty) {
    //如果 city 为空,则使用默认值
    city = '深圳';
  }
  return city;
}

5、创建 store

class _MyHomePageState extends State {
  ...

  final _cityStore = Store(
    changeCityReducer,
    initialState: CityState(null),
    middleware: [readCityFromDisk]
  );
  ...
}

6、使用 StoreProvider 将 store 传给子元素

class _MyHomePageState extends State {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StoreProvider(
        store: _cityStore,
        child: _widgetItems[_selectedIndex], //选中不同的选项显示不同的界面,,
      ),
      ....
    );
  }
  ...
}

7、使用 StoreConnector让子 widget 获取 store 中的数据

class HotWidgetState extends State {
  ...

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    print('HotWidgetState build');
    return StoreConnector(
      converter: (store) {
        String curCity = store.state.curCity;
        if (curCity == null) {
          //如果 curCity 为 null,说明没有初始化,则触发初始化
          store.dispatch(InitCityAction(null));
        }
        return curCity;
      },
      builder: (context, curCity) {
        if (curCity != null && curCity.isNotEmpty) {
          //如果 curCity 不为空
          return ...
        } else {
          //如果 curCity 为空
          return ...
        }
      },
    );
  }

8、发送 action 更新 State

floatingActionButton: new StoreConnector(
            converter: (store) {
              // Return a `VoidCallback`, which is a fancy name for a function
              // with no parameters. It only dispatches an Increment action.
              return () => store.dispatch(Actions.Increment);
            },
            builder: (context, callback) {
              return new FloatingActionButton(
                onPressed: callback,
                tooltip: 'Increment',
                child: new Icon(Icons.add),
              );
            },
          ),

你可能感兴趣的:(Flutter 基础)