Flutter从零到∞学习笔记

时间:2021-04-30 16:37:05
  • 有状态widget:StatefulWidget和无状态widget:StatelessWidget 前者不需要实现Widget build(BuildContext context)。

    具体的选择取决于widget是否需要管理一些状态

  • 在Dart语言中使用下划线前缀标识符,会强制其变成私有的。

  • Icons.favorite Icons类里面有很多默认图标

  • isOdd 是否奇数 2.isOdd -> false 1.isOdd -> true

  • pushSaved “”开头的自动转成私有(方法和变量)

  • 导航栏添加按钮和事件

     @override

    Widget build(BuildContext context) {

      return new Scaffold(

        appBar: new AppBar(

          title: new Text('Startup Name Generator'),

          actions: <Widget>[

            // AppBar 添加一个按钮 样式为list 事件是_pushSaved

            new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved)

          ],

        ),

        body: _buildSuggestions(),

      );

    }

    // tooltip 长时间按下的提示文字

    IconButton(icon: new Icon(Icons.search), tooltip: 'Search', onPressed: null)

  

  1. 界面跳转方法
    Navigator.of(context).push(

      new MaterialPageRoute(

          builder: (context) {

          },

      ),

    );

  

  1. 一行函数写法
    // 多行

    void main() {

      runApp(

          new Center(

            child: new Text(

              'Hello, world!',

              textDirection: TextDirection.ltr,

            ),

          )

      )

    }

    // 一行

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

  

  1. // Material 是UI呈现的“一张纸”

  2. 请确保在pubspec.yaml文件中,将flutter的值设置为:uses-material-design: true。这允许我们可以使用一组预定义Material icons。

  3. Row(横向排列)和Column(纵向排列)

    child: new Row(

      children: <Widget>[

        new ...,

        new ...,

        new ...,

      ],

    )

  

    child: new Column(

      children: <Widget>[

        new ...,

        new ...,

        new ...,

      ],

    ),

  

  1. cached_network_image 图片占位和淡入淡出

  2. push

    Navigator.push(

      context,

      new MaterialPageRoute(builder: (context) => new 新界面),

    );

    // 如果需要传值:

    新界面({Key key, @required this.接收字段的名字}) : super(key: key);

    pop

    Navigator.pop(context);

  

  1. dio网络请求 https://github.com/flutterchina/dio

  2. import 'dart:convert'; // package将响应内容转化为一个json Map

  3. // 使用fromJson工厂函数,将json Map 转化为一个Post对象

new Post.fromJson(json);

  

  1. future参数是一个异步的网络请求

  2. import 'dart:io'; // 添加请求的headers

  3. // 长连接

import 'package:web_socket_channel/io.dart';

import 'package:multi_server_socket/multi_server_socket.dart';

  

  1. // 网络请求
Future<Post> fetchPost() async {

  final response = await http.get('[http://jsonplaceholder.typicode.com/posts/1](http://jsonplaceholder.typicode.com/posts/1)');

  final responseJson = json.decode(response.body);

  return new Post.fromJson(responseJson);

}

// 请求添加headers

/*

Future<Post> fetchPost() async {

  final response = await http.get(

    '[https://jsonplaceholder.typicode.com/posts/1](https://jsonplaceholder.typicode.com/posts/1)',

    headers: {HttpHeaders.AUTHORIZATION: "Basic your_api_token_here"},

  );

  final json = jsonDecode(response.body);

  return new Post.fromJson(json);

}

*/

new FutureBuilder<Post>(

  future: fetchPost(),

  builder: (context, snapshot) {

    return new CircularProgressIndicator();

  }

)

  

  1. 长连接
// 连接长连接

IOWebSocketChannel.connect('[ws://echo](ws://echo/).[websocket.org](http://websocket.org/)’)

// 接收消息

new StreamBuilder(

    stream: widget.channel.stream,

    builder: (context, snapshot) {

      return new Padding(

        child: new Text(snapshot.hasData ? '${snapshot.data}' : ''),

          padding: const EdgeInsets.symmetric(vertical: 20.0)

      );

    }

)

// 发送消息

widget.channel.sink.add(_textController.text);

// 关闭长连接

widget.channel.sink.close();

  

  1. 在Flutter中添加资源和图片

https://flutterchina.club/assets-and-images/

  1. 标准widget:
Container

添加 padding, margins, borders, background color, 或将其他装饰添加到widget.

GridView

将 widgets 排列为可滚动的网格.

ListView

将widget排列为可滚动列表

Stack

将widget重叠在另一个widget之上.

Material Components:

Card

将相关内容放到带圆角和投影的盒子中。

ListTile

将最多3行文字,以及可选的行前和和行尾的图标排成一行

  

  1. pubspec.yaml中添加字体 注意缩进对齐 注意缩进对齐 注意缩进对齐
-asset 路径是与pubspec.yaml平级的文件路径

flutter:

  # Include the Material Design fonts.

  uses-material-design: true

  fonts:

    - family: Rock Salt

      fonts:

        # [https://fonts.google.com/specimen/Rock+Salt](https://fonts.google.com/specimen/Rock+Salt)

      - asset: fonts/Arial-Unicode.ttf

    - family: VT323

      fonts:

        # [https://fonts.google.com/specimen/VT323](https://fonts.google.com/specimen/VT323)

        - asset: fonts/Arial-Unicode.ttf

    - family: Ewert

      fonts:

        # [https://fonts.google.com/specimen/Ewert](https://fonts.google.com/specimen/Ewert)

        - asset: fonts/Ewert-Regular.ttf

  

  1. 比如一个关闭按钮在
new Row(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[

  new FlatButton(onPressed: () {

  }, child: Icon(Icons.close))

],);

  

  1. 分割线
new Divider(color: Colors.lightBlue,) 

  1. 自定义Icon
new Image.asset(“图片路径", width: 20.0, height: 20.0,)

  1. 按钮宽高
001、

new Padding(padding: new EdgeInsets.fromLTRB(48.0, 20.0, 48.0, 20.0),

  child: new Row(

    children: <Widget>[

      new Expanded(child:

        new RaisedButton(onPressed: (){

        },

          //设置控件的高度

          child: new Padding(padding: new EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),

            child: new Text("登录",

              style: TextStyle(color: Colors.white)

            ),

          ),

          color: Colors.brown,

        ),

      ),

    ],

  ),

),

002、

new Container(

  width: MediaQuery.of(context).size.width - 48 * 2 ,

  padding: new EdgeInsets.only(top: 40.0),

  child: new RaisedButton(onPressed: (){

  },

    //设置控件的高度

    child: new Padding(padding: new EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),

      child: new Text("登录",

          style: TextStyle(color: Colors.white)

      ),

    ),

    color: Colors.brown,

  ),

),

003、

Widget _bigButton(String text, double lSpace, double rSpace) {

  return new Container(

    width: MediaQuery.of(context).size.width - lSpace - rSpace,

    height: 48.0,

    margin: new EdgeInsets.only(left: lSpace, right: rSpace),

    color: Colors.white54,

    padding: new EdgeInsets.only(top: 0.0),

    child: new RaisedButton(onPressed: (){

      print(text);

    },

      child: new Padding(padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),

        child: new Text(text,

            style: TextStyle(color: Colors.white)

        ),

      ),

      color: Colors.brown,

    ),

  );

}

  

  1. 设备尺寸
MediaQuery.of(context).size.width

  

  1. 设备像素密度
MediaQuery.of(context).devicePixelRatio

  

  1. 状态栏高度
MediaQuery.of(context).padding.top

  

  1. 担心键盘挡住控件,可以使用 SingleChildScrollView,将SingleChildScrollView当做容器。

  2. 一个超级简单界面

import 'package:flutter/material.dart';

class RegisterPage extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return new Scaffold(

      backgroundColor: Colors.black,

      body: new RegisterWidget(),

    );

  }

}

class RegisterWidget extends StatefulWidget {

  RegisterWidgetState createState() => RegisterWidgetState();

}

class RegisterWidgetState extends State<RegisterWidget> {

  @override

  Widget build(BuildContext context) {

    return new Text("RegisterPage", style: TextStyle(color: Colors.white),);

  }

}

  

  1. Flutter 按钮总结
· InkWell   // 纯文字按钮

· OutLineButton   // 边框按钮

· IconButton  // icon按钮

·

  

  1. import 'package:flutter/services.dart';
TextField

inputFormatters: <TextInputFormatter> [

  WhitelistingTextInputFormatter.digitsOnly,

],

  

37 . 验证码按钮

new Positioned(

    child: new Container(

      width: 80.0,

      height: 27.0,

      alignment: Alignment.center,

      decoration: new BoxDecoration(

        border: new Border.all(

          color: Colors.white,

          width: 1.0,

        ),

        borderRadius: new BorderRadius.circular(4.0),

      ),

      child: InkWell(

        child: _mText(_verifyStr, 12.0),

        onTap: () {

        },

      ),

    )

),

  

  1. 倒计时方法
@override

void dispose() {

  super.dispose();

  _cancelTimer();

}

_startTimer() {

  if (_verifyStr == '重新发送' || _verifyStr == '获取验证码') {

    _seconds = 5;

    _timer = new Timer.periodic(new Duration(seconds: 1), (timer) {

      if (_seconds == 0) {

        _cancelTimer();

        return;

      }

      _seconds--;

      _verifyStr = '$_seconds(s)';

      setState(() {});

      if (_seconds == 0) {

        _verifyStr = '重新发送';

      }

    });

  }

}

_cancelTimer() {

  _timer?.cancel();

}

  

  1. 富文本拼接: 协议
Widget _protocolWidget() {

  return new Container(

    child: new Row(

      children: <Widget>[

        new GestureDetector(

          onTap: () {

            print("选择");

          },

          child: Icon(Icons.add_alert, color: Colors.white),

        ),

        new Text.rich(

            new TextSpan(

                text: '我已阅读并同意',

                style: new TextStyle(

                  fontSize: 12.0,

                  color: Colors.grey[500],

                  fontWeight: FontWeight.w400,

                ),

                children: [

                  new TextSpan(

                    recognizer: new TapGestureRecognizer()

                      ..onTap = () {

                        print("《燎原用户服务协议》");

                      },

                    text: "《燎原用户服务协议》",

                    style: new TextStyle(

                      fontSize: 14.0,

                      color: Color(0XFFB57A36),

                      fontWeight: FontWeight.w400,

                    ),

                  )

                ]

            )

        ),

      ],

    )

  );

}

  

  1. 阴影、圆角
new Card(

  elevation: 4.0,

  shape: new RoundedRectangleBorder(

    borderRadius: BorderRadius.only(

      topLeft: Radius.circular(16.0),

      topRight: Radius.circular(16.0),

      bottomLeft: Radius.circular(12.0),

      bottomRight: Radius.circular(2.0),

    )

  ),

  child: new IconButton(icon: Icon(Icons.add), onPressed: () {

  }),

)

  

  1. YYTabbarWidget
import 'package:flutter/material.dart';

// with AutomaticKeepAliveClientMixin

class YYTabbarWidget extends StatefulWidget {

  List<Widget> tabItems = [];

  Widget title;

  List<Widget> tabViews = [];

  PageController pageController;

  final ValueChanged<int> onPageChanged;

  final Widget drawer;

  YYTabbarWidget({Key key,

    this.drawer,

    this.tabItems,

    this.title,

    this.tabViews,

    this.pageController,

    this.onPageChanged,

  }) : super(key: key);

  _YYTabbarWidgetState createState() => _YYTabbarWidgetState(drawer, title, tabItems, tabViews, pageController, onPageChanged);

}

class _YYTabbarWidgetState extends State<YYTabbarWidget> with SingleTickerProviderStateMixin {

  final Widget _title;

  final List<Widget> _tabViews;

  final List<Widget> _tabItems;

  final ValueChanged<int> _onPageChanged;

  final Widget _drawer;

  _YYTabbarWidgetState(

      this._drawer,

      this._title,

      this._tabItems,

      this._tabViews,

      this._pageController,

      this._onPageChanged,

      ) : super();

  TabController _tabController;

  PageController _pageController;

  @override

  void initState() {

    super.initState();

    _tabController = new TabController(length: _tabItems.length, vsync: this);

  }

  @override

  void dispose() {

    _tabController.dispose();

    super.dispose();

  }

  _renderTab() {

    print(_tabItems);

    List<Widget> list = new List();

    for (int i = 0; i < _tabItems.length; i++) {

      list.add(new FlatButton(onPressed: () {

        print(i);

        _pageController.jumpTo(MediaQuery

            .of(context)

            .size

            .width * i);

      }, child: _tabItems[I],

      )

      );

    }

    return list;

  }

  @override

  Widget build(BuildContext context) {

    return new Scaffold(

      drawer: _drawer,

      appBar: new AppBar(

        title: _title,

      ),

      body: new PageView(

        controller: _pageController,

        children: _tabViews,

        onPageChanged: (index) {

          _tabController.animateTo(index);

          _onPageChanged?.call(index);

        },

    ),

      bottomNavigationBar: new Material(

        color: Colors.white,

        child: new TabBar(

          indicatorPadding: new EdgeInsets.only(top: 0.0),

          controller: _tabController,

          tabs: _renderTab(),

          indicatorColor: Colors.red,

        ),

      ),

    );

  }

}

  

  1. ListView 添加刷新,当数量少的时候不能滚动
physics: new AlwaysScrollableScrollPhysics(), // 让ListView一直可以滚动

  

  1. tabView切换 子界面都会调用initState
解决:AutomaticKeepAliveClientMixin

class HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {

    @override

    bool get wantKeepAlive => true;

}

  

  1. 路有跳转
///不带参数的路由表跳转

Navigator.pushNamed(context,routeName);

///跳转新页面并且替换,比如登录页跳转主页

Navigator.pushReplacementNamed(context,routeName);

///跳转到新的路由,并且关闭给定路由的之前的所有页面

Navigator.pushNamedAndRemoveUntil(context,'/calendar',ModalRoute.withName('/'));

///带参数的路由跳转,并且监听返回

Navigator.push(context,newMaterialPageRoute(builder:(context)=>newNotifyPage())).then((res){

      ///获取返回处理

    });

  

  1. flutter lib
cupertino_icons: ^0.1.2  #icon

flutter_spinkit: "^2.1.0"  # load more loading  import 'package:flutter_spinkit/flutter_spinkit.dart';

dio: x.x.x  #无网络请求  import 'package:dio/dio.dart';

  

  1. dio网络请求示例
_dioRequest() async {

  Dio dio = new Dio();

  Response response;

  try {

    String url;

    var params; // 请求参数

    Options options; // 配置:超时,请求头,请求类型等

    response = await dio.request(url, data: params, options: options);

  } on DioError catch(e) {

    // 请求出错时,返回一个DioError对象

  }

}

  

  1. build_runner的使用
1、在根目录运行

2、一次性创建.g.dart文件 使用build 此时目录内不能有.g.dart文件

3、watch是监听 有model类的文件创建 自动创建.g.dart文件

flutter packages pub run build_runner build

flutter packages pub run build_runner watch