当前位置:澳门贵宾厅 > 澳门贵宾厅 > 早在一年前想学习下flutter澳门贵宾厅,具体的选择取决于widget是否需要管理一些状态
早在一年前想学习下flutter澳门贵宾厅,具体的选择取决于widget是否需要管理一些状态
2020-03-14

时间: 2019-12-27阅读: 71标签: Flutter作者:星星y,链接:哎,Flutter真香啊

  1. 有状态widget:StatefulWidget和无状态widget:StatelessWidget 前者不需要实现Widget build(BuildContext context)。

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

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

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

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

  5. pushSaved “”开头的自动转成私有

  6. 导航栏添加按钮和事件

早在一年前想学习下flutter,但当时对于它布局中地狱式的嵌套有点望而生畏,心想为什么嵌套这么复杂,就没有xml布局方式吗,用jsx方式也行啊,为什么要用dart而不用javascript,走开,劳资不学了。

然而,随着今年google io大会flutter新版本发布,大势宣扬。我又开始从头学习flutter了:

 @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. 界面跳转方法

本想看下视频实战的,后面发现效率太低(有点啰嗦),放弃了。最终还是决定通过阅读flutter项目源码学习,事实上还是这种效率最高。

刚好公司有新app开发,这次决定用flutter开发了,边开发边学习,既完成了工作又完成了学习(ps:现在公司ios和前端也在学了:joy:)。

 Navigator.of.push( new MaterialPageRoute( builder:  { }, ), );

用完flutter的感受是,一旦接受这种嵌套布局后,发现布局也没那么难,hot reload牛皮,async真好用,dart语言真方便,嗯,香啊。

  1. 一行函数写法

下面就此次app开发记录相关要点(菜鸟阶段,欢迎指正)

第三方库

 // 多行 void main() { runApp( new Center( child: new Text( 'Hello, world!', textDirection: TextDirection.ltr, ), ) ) } // 一行 void main() => runApp(new MyApp;

dio: 网络

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

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

  3. Row和Column

sqflite: 数据库

pull_to_refresh: 下拉刷新,上拉加载

 child: new Row( children: <Widget>[ new ..., new ..., new ..., ], )

 child: new Column( children: <Widget>[ new ..., new ..., new ..., ], ),

json_serializable: json序列化,自动生成model工厂方法

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

  2. push

shared_preferences: 本地存储

fluttertoast: 吐司消息

 Navigator.push( context, new MaterialPageRoute(builder:  => new 新界面), ); // 如果需要传值: 新界面({Key key, @required this.接收字段的名字}) : super; pop Navigator.pop;

图片资源

  1. dio网络请求

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

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

为适配各个分辨率的图片资源,通常需要1,2,3倍的图。在flutter项目根目录下创建assets/images目录,在pubspec.yaml文件中加入图片配置

flutter: # ... assets: - assets/images/
new Post.fromJson;

然后通过sketch切出1/2/3倍图片,这里可通过编辑预设,在词首加入2.0x/和3.0x/,这样导出的格式便符合flutter图片资源所需了。

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

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

  3. // 长连接

这里再建一个image_helper.dart的工具类,用于产生Image

class ImageHelper { static String png(String name) { return "assets/images/$name.png"; } static Widget icon(String name, {double width, double height, BoxFit boxFit}) { return Image.asset( png(name), width: width, height: height, fit: boxFit, ); }}
import 'package:web_socket_channel/io.dart';import 'package:multi_server_socket/multi_server_socket.dart';

主界面Tab导航

  1. // 网络请求

在app主界面,tab底部导航是最常用的。通常基于Scaffold的bottomNavigationBar配和PageView使用。通过PageController控制PageView界面切换,同时使用BottomNavigationBar的currentIndex控制tab选中状态。

为了能使监听返回键,使用WillPopScope实现点两次返回键退出app。

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;}*/new FutureBuilder<Post>( future: fetchPost(), builder: (context, snapshot) { return new CircularProgressIndicator
List pages = Widget[HomePage(), MinePage()];class _TabNavigatorState extends StateTabNavigator { DateTime _lastPressed; int _tabIndex = 0; var _controller = PageController(initialPage: 0); BottomNavigationBarItem buildTab( String name, String normalIcon, String selectedIcon) { return BottomNavigationBarItem( icon: ImageHelper.icon(normalIcon, width: 20), activeIcon: ImageHelper.icon(selectedIcon, width: 20), title: Text(name)); } @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BottomNavigationBar( currentIndex: _tabIndex, backgroundColor: Colors.white, onTap: (index) { setState(() { _controller.jumpToPage(index); _tabIndex = index; }); }, selectedItemColor: Color(0xff333333), unselectedItemColor: Color(0xff999999), selectedFontSize: 11, unselectedFontSize: 11, type: BottomNavigationBarType.fixed, items: [ buildTab("Home", "ic_home", "ic_home_s"), buildTab("Mine", "ic_mine", "ic_mine_s") ]), body: WillPopScope( child: PageView.builder( itemBuilder: (ctx, index) = pages[index], controller: _controller, physics: NeverScrollableScrollPhysics(),//禁止PageView左右滑动 ), onWillPop: () async { if (_lastPressed == null || DateTime.now().difference(_lastPressed)  Duration(seconds: 1)) { _lastPressed = DateTime.now(); Fluttertoast.showToast(msg: "Press again to exit"); return false; } else { return true; } }), ); }}
  1. 长连接

网络层封装

网络框架使用的是dio,不管是哪种平台,网络请求最终要转成实体model用于ui展示。这里先将dio做一个封装,便于使用。

// 连接长连接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中添加资源和图片

网络请求中通常需要添加自定义拦截器来预处理网络请求,往往需要将登录信息(如user_id等)放在公共参数中,例如;

import 'package:dio/dio.dart';import 'dart:async';import 'package:shared_preferences/shared_preferences.dart';class CommonInterceptor extends Interceptor { @override Future onRequest(RequestOptions options) async { options.queryParameters = options.queryParameters ?? {}; options.queryParameters["app_id"] = "1001"; var pref = await SharedPreferences.getInstance(); options.queryParameters["user_id"] = pref.get(Constants.keyLoginUserId); options.queryParameters["device_id"] = pref.get(Constants.keyDeviceId); return super.onRequest(options); }}
  1. 标准widget:

Dio封装

然后使用dio封装get和post请求,预处理响应response的code。假设我们的响应格式是这样的:

Container添加 padding, margins, borders, background color, 或将其他装饰添加到widget.GridView将 widgets 排列为可滚动的网格.ListView将widget排列为可滚动列表Stack将widget重叠在另一个widget之上.Material Components:Card将相关内容放到带圆角和投影的盒子中。ListTile将最多3行文字,以及可选的行前和和行尾的图标排成一行
{ code:0, msg:"获取数据成功", result:[] //或者{}}

import 'package:dio/dio.dart';import 'common_interceptor.dart';/* * 网络管理 */class HttpManager { static HttpManager _instance; static HttpManager getInstance() { if (_instance == null) { _instance = HttpManager(); } return _instance; } Dio dio = Dio(); HttpManager() { dio.options.baseUrl = ""; dio.options.connectTimeout = 10000; dio.options.receiveTimeout = 5000; dio.interceptors.add(CommonInterceptor()); dio.interceptors.add(LogInterceptor(responseBody: true)); } static FutureMapString, dynamic get(String path, MapString, dynamic map) async { var response = await getInstance().dio.get(path, queryParameters: map); return processResponse(response); } /* 表单形式 */ static FutureMapString, dynamic post(String path, MapString, dynamic map) async { var response = await getInstance().dio.post(path, data: map, options: Options( contentType: "application/x-www-form-urlencoded", headers: {"Content-Type": "application/x-www-form-urlencoded"})); return processResponse(response); } static FutureMapString, dynamic processResponse(Response response) async { if (response.statusCode == 200) { var data = response.data; int code = data["code"]; String msg = data["msg"]; if (code == 0) {//请求响应成功 return data; } throw Exception(msg); } throw Exception("server error"); }}
  1. pubspec.yaml中添加字体 注意缩进对齐 注意缩进对齐 注意缩进对齐

map转model

使用dio可以将最终的请求响应response转成MapString, dynamic对象,我们还需要将map转成相应的model。假如我们有一个获取文章列表的接口响应如下:

-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
{ code:0, msg:"获取数据成功", result:[ { article_id:1, article_title:"标题", article_link:"" } ]}
  1. 比如一个关闭按钮在

就需要一个Article的model。由于Flutter下是禁用反射的,我们只能手动初始化每个成员变量。

不过我们可以通过json_serializable将手动初始化的工作交给它。首先在pubspec.yaml引入它:

new Row(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ new FlatButton(onPressed: () { }, child: Icon(Icons.close))],);
dependencies: json_annotation: ^2.0.0dev_dependencies: json_serializable: ^2.0.0
  1. 分割线

我们创建一个article.dart的model类:

import 'package:json_annotation/json_annotation.dart';part 'article.g.dart';//FieldRename.snake 表示json字段下划线分割类型如:article_id@JsonSerializable(fieldRename: FieldRename.snake, checked: true)class Article { final int articleId; final String articleTitle; final String articleLikn;}
new Divider(color: Colors.lightBlue,) 

注意这里引用到了一个article.g.dart没有产生的文件,我们通过pub run build_runner build命令就会生成这个文件

  1. 自定义Icon
// GENERATED CODE - DO NOT MODIFY BY HANDpart of 'article.dart';// **************************************************************************// JsonSerializableGenerator// **************************************************************************Article _$ArticleFromJson(MapString, dynamic json) { return $checkedNew('Article', json, () { final val = Article(); $checkedConvert(json, 'article_id', (v) = val.articleId = v as int); $checkedConvert( json, 'article_title', (v) = val.articleTitle = v as String); $checkedConvert(json, 'article_link', (v) = val.articleLink = v as String); return val; }, fieldKeyMap: const { 'articleId': 'article_id', 'articleTitle': 'article_title', 'articleLink': 'article_link' });}MapString, dynamic _$ArticleToJson(Article instance) = String, dynamic{ 'article_id': instance.articleId, 'article_title': instance.articleTitle, 'article_link': instance.articleLink };

然后在article.dart里添加工厂方法

new Image.asset(“图片路径", width: 20.0, height: 20.0,)
class Article{ ... factory Article.fromJson(MapString, dynamic json) = _$ArticleFromJson(json);}
  1. 按钮宽高

具体请求封装

创建好model类后,就可以建一个具体的api请求类ApiRepository,通过async库,可以将网络请求最终封装成一个Future对象,实际调用时,我们可以将异步回调形式的请求转成同步的形式,这有点和kotlin的协程类似:

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.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.size.width - lSpace - rSpace, height: 48.0, margin: new EdgeInsets.only(left: lSpace, right: rSpace), color: Colors.white54, padding: new EdgeInsets.only, child: new RaisedButton(onPressed: (){ print; }, 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, ), );}
import 'dart:async';import '../_manager.dart';import '../model/article.dart';class ApiRepository { static FutureListArticle articleList() async { var data = await HttpManager.get("articleList", {"page": 1}); return data["result"].map((MapString, dynamic json) { return Article.fromJson(json); }); }}
  1. 设备尺寸

实际调用

封装好网络请求后,就可以在具体的组件中使用了。假设有一个_ArticlePageState:

MediaQuery.of.size.width
import 'package:flutter/material.dart';import '../model/article.dart';import '../repository/api_repository.dart';class ArticlePage extends StatefulWidget { @override StateStatefulWidget createState() { return _ArticlePageState(); }}class _ArticlePageState extends StateArticlePage { ListArticle _list = []; @override void initState() { super.initState(); _loadData(); } void _loadData() async {//如果需要展示进度条,就必须try/catch捕获请求异常。 showLoading(); try { var list = await ApiRepository.articleList(); setState(() { _list = list; }); } catch (e) {} hideLoading(); } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: ListView.builder( itemCount: _list.length, itemBuilder: (ctx, index) { return Text(_list[index].articleTitle); })), ); }}
  1. 设备像素密度

数据库

数据库操作通过sqflite,简单封装处理事例了文章Article的插入操作。

MediaQuery.of.devicePixelRatio
import 'package:sqflite/sqflite.dart';import 'package:path/path.dart';import 'dart:async';import '../model/article.dart';class DBManager { static const int _VSERION = 1; static const String _DB_NAME = "database.db"; static Database _db; static const String TABLE_NAME = "t_article"; static const String createTableSql = ''' create table $TABLE_NAME( article_id int, article_title text, article_link text, user_id int, primary key(article_id,user_id) ); '''; static init() async { String dbPath = await getDatabasesPath(); String path = join(dbPath, _DB_NAME); _db = await openDatabase(path, version: _VSERION, onCreate: _onCreate); } static _onCreate(Database db, int newVersion) async { await db.execute(createTableSql); } static Futureint insertArticle(Article item, int userId) async { var map = item.toMap(); map["user_id"] = userId; return _db.insert("$TABLE_NAME", map); }}
  1. 状态栏高度

Android层兼容通信处理

为了兼容底层,需要通过MethodChannel进行Flutter和Native(Android/iOS)通信

MediaQuery.of.padding.top

flutter调用Android层方法

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

  2. 一个超级简单界面

这里举例flutter端打开系统相册意图,并取得最终的相册路径回调给flutter端。

我们在Android中的MainActivity中onCreate方法处理通信逻辑

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),); }}
eventChannel = MethodChannel(flutterView, "event") eventChannel?.setMethodCallHandler { methodCall, result - when (methodCall.method) { "openPicture" - PictureUtil.openPicture(this) { result.success(it) } } }
  1. Flutter 按钮总结

因为是通过result.success将结果回调给Flutter端,所以封装了打开相册的工具类。

object PictureUtil { fun openPicture(activity: Activity, callback: (String?) - Unit) { val f = getFragment(activity) f.callback = callback val intentToPickPic = Intent(Intent.ACTION_PICK, null) intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") f.startActivityForResult(intentToPickPic, 200) } private fun getFragment(activity: Activity): PictureFragment { var fragment = activity.fragmentManager.findFragmentByTag("picture") if (fragment is PictureFragment) { } else { fragment = PictureFragment() activity.fragmentManager.apply { beginTransaction().add(fragment, "picture").commitAllowingStateLoss() executePendingTransactions() } } return fragment }}
· InkWell // 纯文字按钮· OutLineButton // 边框按钮· IconButton // icon按钮· 

然后在PictureFragment中加入callback,并且处理onActivityResult逻辑

  1. import 'package:flutter/services.dart';
class PictureFragment : Fragment() { var callback: ((String?) - Unit)? = null override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 200) { if (data != null) { callback?.invoke(FileUtil.getFilePathByUri(activity, data!!.data)) } } }}

这里FileUtil.getFilePathByUri是通过data获取相册路径逻辑就不贴代码了,网上很多可以搜索一下。

TextFieldinputFormatters: <TextInputFormatter> [ WhitelistingTextInputFormatter.digitsOnly,],

然后在flutter端使用

  1. 以上已经添加简书

  2. 验证码按钮

void _openPicture() async { var result = await MethodChannel("event").invokeMethod("openPicture"); images.add(result as String); setState(() {}); }

Android端调用Flutter代码

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, child: InkWell( child: _mText(_verifyStr, 12.0), onTap: , )),

将刚刚MainActivity中的eventChannel声明成类变量,就可以在其他地方使用它了。比如推送通知,如果需要调用Flutter端的埋点接口方法。

  1. 倒计时方法
class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) eventChannel = MethodChannel(flutterView, "event") eventChannel?.setMethodCallHandler { methodCall, result - ... } } checkNotify(intent) initPush() } companion object { var eventChannel: MethodChannel? = null }}

在Firebase消息通知中调用Flutter方法

@overridevoid dispose() { super.dispose(); _cancelTimer();}_startTimer() { if (_verifyStr == '重新发送' || _verifyStr == '获取验证码') { _seconds = 5; _timer = new Timer.periodic(new Duration(seconds: 1),  { if (_seconds == 0) { _cancelTimer(); return; } _seconds--; _verifyStr = '$_seconds'; setState; if (_seconds == 0) { _verifyStr = '重新发送'; } }); }}_cancelTimer() { _timer?.cancel();}
class FirebaseMsgService : FirebaseMessagingService() { override fun onMessageReceived(msg: RemoteMessage?) { super.onMessageReceived(msg) "onMessageReceived:$msg".logE() if (msg != null){ showNotify(msg) MainActivity.eventChannel?.invokeMethod("saveEvent", 1) } }}
  1. 富文本拼接: 协议

然后在Flutter层我们添加回调

class NativeEvent { static const platform = const MethodChannel("event"); static void init() { platform.setMethodCallHandler(platformCallHandler); } static Futuredynamic platformCallHandler(MethodCall call) async { switch (call.method) { case "saveEvent": print("saveEvent....."); await ApiRepository.saveEventTracking(call.arguments); return ""; break; } }}
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, topRight: Radius.circular, bottomLeft: Radius.circular, bottomRight: Radius.circular, child: new IconButton(icon: Icon(Icons.add), onPressed: 
  1. YYTabbarWidget
import 'package:flutter/material.dart';// with AutomaticKeepAliveClientMixinclass 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; _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; _pageController.jumpTo(MediaQuery .of .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:  { _tabController.animateTo; _onPageChanged?.call; }, ), bottomNavigationBar: new Material( color: Colors.white, child: new TabBar( indicatorPadding: new EdgeInsets.only, controller: _tabController, tabs: _renderTab(), indicatorColor: Colors.red, ), ), ); }}
  1. ListView 添加刷新,当数量少的时候不能滚动
physics: new AlwaysScrollableScrollPhysics(), // 让ListView一直可以滚动
  1. tabView切换 子界面都会调用initState
解决:AutomaticKeepAliveClientMixinclass 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:=>newNotifyPage.then{ ///获取返回处理 });
  1. flutter lib
cupertino_icons: ^0.1.2 #iconflutter_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 { // 请求出错时,返回一个DioError对象 }}
  1. build_runner的使用
1、在根目录运行2、一次性创建.g.dart文件 使用build 此时目录内不能有.g.dart文件3、watch是监听 有model类的文件创建 自动创建.g.dart文件flutter packages pub run build_runner buildflutter packages pub run build_runner watch
上一篇:没有了