/ 没有感情万千 、只有默默无闻 /

2022年跨年了,又涨了一岁。随着时光的流逝,工作多年的我还是在坚持些代码。互联网都有所谓的大龄危机,我对此毫无畏惧。不要因为社会存在一些大龄危机的恐慌,产生了很多心理上的负担 ,我虽然不再年少轻狂,但激情依旧。

你需要懂的法则就是 : 适者生存,优胜劣汰 。

你朝思暮想的结果就是 : 冰冻三尺,非一日之寒 。

你想太多的结果就是 : 还是在原点 , 人老了 , 留下的不是遗憾就是回忆 。

................

/  异步 UI 更新FutureBuilder  /

当我们打开app在网络畅通的情况下从服务器获取数据 , 同时获取数据需要时间 ,这时我们需要一个可以体现数据请求过程的进度条 , 获取到数据后渲染页面 。

FutureBuilder 是继承自 StatefulWidget  builder必须要有返回值 , 不能为空。

 const FutureBuilder({Key? key,this.future,this.initialData,required this.builder,}) : assert(builder != null),super(key: key);

final Future<T>? future;

/// The asynchronous computation to which this builder is currently connected,/// possibly null.////// If no future has yet completed, including in the case where [future] is/// null, the data provided to the [builder] will be set to [initialData].

builder 与异步请求建立的连接 (connected) 可能为空 (即future参数可能为空) 。如果异步请求没有完成 , 包括future参数为空 ,那么就提供默认数据(initialData) 给 builder使用 。

当future 不为空时

class _MyHomePageState extends State<MyHomePage> {var _futBuiHom;void _handleGetNetData() async {await _getNetData();setState(() {});}Future<String> _getNetData() async {return await Future.delayed(Duration(seconds: 2), () => "从互联网上获取的数据111111");}@overridevoid initState() {// TODO: implement initStatesuper.initState();_futBuiHom = _getNetData();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: FutureBuilder<dynamic>(future: _futBuiHom,initialData: '初始化的数据',builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {print('\n获取到到数据: State${snapshot.connectionState} \n data${snapshot.data}');switch (snapshot.connectionState) {case ConnectionState.none:case ConnectionState.waiting:return const Text('数据加载中.....'); //加载中default: //如果_calculation执行完毕if (snapshot.hasError) {//若_calculation执行出现异常return Text('Error: ${snapshot.hasError}');} else {//若_calculation执行正常完成return Text('Error: ${snapshot.data}');}}},),),floatingActionButton: FloatingActionButton(onPressed: _handleGetNetData,tooltip: '模拟从网络获取数据',child: Icon(Icons.add),),);}
}

 当Future 为空时

final AsyncWidgetBuilder<T> builder;

  /// The build strategy currently used by this builder.////// The builder is provided with an [AsyncSnapshot] object whose/// [AsyncSnapshot.connectionState] property will be one of the following/// values://////  * [ConnectionState.none]: [future] is null. The [AsyncSnapshot.data] will///    be set to [initialData], unless a future has previously completed, in///    which case the previous result persists.//////  * [ConnectionState.waiting]: [future] is not null, but has not yet///    completed. The [AsyncSnapshot.data] will be set to [initialData],///    unless a future has previously completed, in which case the previous///    result persists.//////  * [ConnectionState.done]: [future] is not null, and has completed. If the///    future completed successfully, the [AsyncSnapshot.data] will be set to///    the value to which the future completed. If it completed with an error,///    [AsyncSnapshot.hasError] will be true and [AsyncSnapshot.error] will be///    set to the error object.////// This builder must only return a widget and should not have any side/// effects as it may be called multiple times.

builder提供了一个AsyncSnapshot 对象 , 该对象可以获取异步请求的状态 。

必须返回一个Widget , 因为builder 函数会被调用多次 。

awaiting状态下显示异步请求进度条 。

done 状态下 , 如果hasError 为false , 就重新渲染Widget 。

final T? initialData

/// The data that will be used to create the snapshots provided until a/// non-null [future] has completed.////// If the future completes with an error, the data in the [AsyncSnapshot]/// provided to the [builder] will become null, regardless of [initialData]./// (The error itself will be available in [AsyncSnapshot.error], and/// [AsyncSnapshot.hasError] will be true.)

FutureBuilder 初始化数据 . 当future参数不为空 (存在异步请求时) , 异步请求完成后 builder 函数将会被回调 .

/  FutureBuilder 源码讲解/

开始订阅 (异步获取数据)

  void _subscribe() {if (widget.future != null) {final Object callbackIdentity = Object();_activeCallbackIdentity = callbackIdentity;widget.future!.then<void>((T data) {if (_activeCallbackIdentity == callbackIdentity) {setState(() {_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);});}}, onError: (Object error, StackTrace stackTrace) {if (_activeCallbackIdentity == callbackIdentity) {setState(() {_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error, stackTrace);});}});_snapshot = _snapshot.inState(ConnectionState.waiting);}}

当父视图调用 setState 函数时,子视图的 didUpdateWidget 函数被调用 .  如果 future 的值直接引用获取数据的异步函数, 那么builder 对应的函数要被调用多次 .

class _MyHomePageState extends State<MyHomePage> {var _futBuiHom;void _handleGetNetData() async {await _getNetData();setState(() {});}Future<String> _getNetData() async {return await Future.delayed(Duration(seconds: 2), () => "从互联网上获取的数据111111");}@overridevoid initState() {// TODO: implement initStatesuper.initState();_futBuiHom = _getNetData();}@overridevoid didUpdateWidget(covariant MyHomePage oldWidget) {// TODO: implement didUpdateWidgetsuper.didUpdateWidget(oldWidget);print('didUpdateWidget');}@overrideWidget build(BuildContext context) {print('build(BuildContext context)');return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: FutureBuilder<dynamic>(future: _getNetData(),initialData: '初始化的数据',builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {print('\n获取到到数据: State${snapshot.connectionState} \n data${snapshot.data}');switch (snapshot.connectionState) {case ConnectionState.none:case ConnectionState.waiting:return const Text('数据加载中.....'); //加载中default: //如果_calculation执行完毕if (snapshot.hasError) {//若_calculation执行出现异常return Text('Error: ${snapshot.hasError}');} else {//若_calculation执行正常完成return Text('Error: ${snapshot.data}');}}},),),floatingActionButton: FloatingActionButton(onPressed: _handleGetNetData,tooltip: '模拟从网络获取数据',child: Icon(Icons.add),),);}
}

在FutureBuilder 状态管理类 _FutureBuilderState 的 didUpdateWidget 打印 当前future和上一次的future实例是否一样 .

结果显示不一样

 所以为了让每次异步请求的实例是同一个 future ,需要在Widget 创建时在initState 函数里面初始化异步请求的future , 每次FutureBuilder 重新构建时都从原来的future异步请求中获取数据.

 var _futBuiHom;Future<String> _getNetData() async {return await Future.delayed(Duration(seconds: 2), () => "从互联网上获取的数据111111");}@overridevoid initState() {// TODO: implement initStatesuper.initState();_futBuiHom = _getNetData();}

每次调用setState刷新界面

 void _handleGetNetData() async {await _getNetData();setState(() {});}

/ FutureBuilder + Dio +MVP 实现网络请求 /

Flutter 项目实战 Dio网络请求 四

import 'dart:collection';
import 'dart:core';import 'package:connectivity/connectivity.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';import 'http_error.dart';///http请求成功回调
typedef HttpSuccessCallback<T> = void Function(dynamic data);///失败回调
typedef HttpFailureCallback = void Function(HttpError data);///数据解析回调
typedef T JsonParse<T>(dynamic data);class HttpManager {///同一个CancelToken可以用于多个请求,当一个CancelToken取消时,///所有使用该CancelToken的请求都会被取消,一个页面对应一个CancelToken。Map<String, CancelToken> _cancelTokens = Map<String, CancelToken>();///超时时间static const int CONNECT_TIMEOUT = 30000;static const int RECEIVE_TIMEOUT = 30000;/// http request methodsstatic const String GET = 'get';static const String POST = 'post';Dio? _client;static final HttpManager _instance = HttpManager._internal();factory HttpManager() => _instance;Dio get client => _client!;/// 创建 dio 实例对象HttpManager._internal() {if (_client == null) {/// 全局属性:请求前缀、连接超时时间、响应超时时间BaseOptions options = BaseOptions(connectTimeout: CONNECT_TIMEOUT,receiveTimeout: RECEIVE_TIMEOUT,);_client = Dio(options);}}///初始化公共属性/// [baseUrl] 地址前缀/// [connectTimeout] 连接超时赶时间/// [receiveTimeout] 接收超时赶时间/// [interceptors] 基础拦截器void init({String? baseUrl,int? connectTimeout,int? receiveTimeout,List<Interceptor>? interceptors}) {_client!.options = _client!.options.copyWith(baseUrl: baseUrl,connectTimeout: connectTimeout,receiveTimeout: receiveTimeout,);if (interceptors != null && interceptors.isNotEmpty) {_client!.interceptors..addAll(interceptors);}}///Get网络请求///[url] 网络请求地址不包含域名///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求get({String? url,Map<String, dynamic>? params,Options? options,HttpSuccessCallback? successCallback,HttpFailureCallback? errorCallback,String? tag,}) async {return _request(url: url,params: params,method: GET,options: options,successCallback: successCallback!,errorCallback: errorCallback!,tag: tag!,);}///post网络请求///[url] 网络请求地址不包含域名///[data] post 请求参数///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求post({String? url,data,Map<String, dynamic>? params,Options? options,HttpSuccessCallback? successCallback,HttpFailureCallback? errorCallback,@required String? tag,}) async {return _request(url: url!,data: data,method: POST,params: params!,options: options!,successCallback: successCallback!,errorCallback: errorCallback!,tag: tag!,);}///统一网络请求///[url] 网络请求地址不包含域名///[data] post 请求参数///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求_request({String? url,String? method,data,Map<String, dynamic>? params,Options? options,HttpSuccessCallback? successCallback,HttpFailureCallback? errorCallback,@required String? tag,}) async {//检查网络是否连接ConnectivityResult connectivityResult =await (Connectivity().checkConnectivity());if (connectivityResult == ConnectivityResult.none) {if (errorCallback != null) {errorCallback(HttpError(HttpError.NETWORK_ERROR, "网络异常,请稍后重试!"));}return;}//设置默认值params = params ?? {};method = method ?? 'GET';options?.method = method;options = options ??Options(method: method,);///请求头options.headers = await _headers();try {CancelToken cancelToken;cancelToken =(_cancelTokens[tag] == null ? CancelToken() : _cancelTokens[tag])!;_cancelTokens[tag!] = cancelToken;Response response = await _client!.request(url!,data: data,queryParameters: params,options: options,cancelToken: cancelToken);var _responseData = response.data;print('相应数据:$_responseData');return _responseData;/*int statusCode = _responseData["code"];if (statusCode == 200) {//成功successCallback!(_responseData["data"]);} else {//失败String message = _responseData["msg"].toString();errorCallback!(HttpError('$statusCode', message));}*/} on DioError catch (e, s) {if (e.type != DioErrorType.cancel) {errorCallback!(HttpError.dioError(e));}} catch (e, s) {errorCallback!(HttpError(HttpError.UNKNOWN, "未知错误,请稍后重试!"));}}///取消网络请求void cancel(String tag) {if (_cancelTokens.containsKey(tag)) {if (!_cancelTokens[tag]!.isCancelled) {_cancelTokens[tag]!.cancel();}_cancelTokens.remove(tag);}}///请求头Future<Map<String, String>> _headers() async {Map<String, String> _headers = new HashMap();String _token = '';_headers.addAll({"token": _token});return _headers;}
}

MVP 模式 - 模型层 - 基础类

创建IModel

import 'package:flutter_dio_mvp_futurebuilder/base/http/http_error.dart';abstract class IModel {///释放网络请求void dispose();
}typedef SuccessCallback<T> = void Function(dynamic data);typedef FailureCallback = void Function(HttpError error);

AbstractModel

import 'IModel.dart';
abstract class AbstractModel implements IModel {String? _tag;String? get tag => _tag;AbstractModel() {_tag = '${DateTime.now().millisecondsSinceEpoch}';}}

MVP模式 - Presenter基础类

IPresenter

import 'package:flutter_dio_mvp_futurebuilder/base/view/IView.dart';abstract class IPresenter<V extends IView> {void attachView(V view);void detachView();
}

AbstractPresenter

import 'package:flutter_dio_mvp_futurebuilder/base/model/IModel.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/view/IView.dart';import 'IPresenter.dart';abstract class AbstractPresenter<V extends IView , M extends IModel >implements IPresenter {M? _model;V? _view;@overridevoid attachView(IView  view) {this._model = createModel() as M?;this._view = view as V?;}@overridevoid detachView() {if (_view != null) {_view = null;}if (_model != null) {_model!.dispose();_model = null;}}V? get view {return _view;}//  V get view => _view;M? get model => _model;IModel  createModel();}

IView

class IView {///开始加载void startLoading() {}///加载成功void showLoadSuccess() {}///加载失败void showLoadFailure(String code, String message) {}}

BaseView

import 'package:flutter/material.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/presenter/IPresenter.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/view/IView.dart';abstract class BaseView extends StatefulWidget {BaseView({Key? key}) : super(key: key);@overrideBaseViewState createState() => getState();///子类实现BaseViewState getState();
}abstract class BaseViewState<P extends IPresenter, V extends BaseView>extends State<V> with IView {P? presenter;@overridevoid initState() {super.initState();presenter = createPresenter();if (presenter != null) {presenter?.attachView(this);}}P? createPresenter() {}@overrideWidget build(BuildContext context) {return Scaffold(///导航栏appBar: buildAppBar(),///内容区域body: buildWidget(),///内容区域背景颜色backgroundColor: buildBodyColor(),);}buildWidget();buildAppBar() => null;Color buildBodyColor() {return Color(0xff00FFFFFF);}
}

创建用于业务逻辑处理的callback

import 'package:flutter_dio_mvp_futurebuilder/base/model/IModel.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/presenter/IPresenter.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/view/IView.dart';abstract class CDataModel extends IModel {///loadData(Map<String, dynamic> p, SuccessCallback s, FailureCallback f);
}abstract class CDataPresenter extends IPresenter {///loadData(Map<String, dynamic> p);
}abstract class CDataView extends IView {///loadData(d);
}

模型层(Model)、Presenter、视图层(View) 的具体实现

模型层(Model) 实现

import 'package:flutter_dio_mvp_futurebuilder/base/http/http_manager.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/model/AbstractModel.dart';
import 'package:flutter_dio_mvp_futurebuilder/busmer/mvp_callback.dart';class MData extends AbstractModel implements CDataModel {@overridevoid dispose() {// TODO: implement disposeHttpManager().cancel(tag!);}@overrideloadData(Map<String, dynamic> p, s, f) async{return HttpManager().get(///网络请求地址url: '/f',tag: tag!,successCallback: (data) {s(data);},errorCallback: (data) {f(data);},params: p);}
}

Presenter 实现

import 'package:flutter_dio_mvp_futurebuilder/base/model/IModel.dart';
import 'package:flutter_dio_mvp_futurebuilder/base/presenter/AbstractPresenter.dart';
import 'package:flutter_dio_mvp_futurebuilder/busmer/mvp_callback.dart';
import 'package:flutter_dio_mvp_futurebuilder/model/m_data.dart';class PData extends AbstractPresenter<CDataView, CDataModel>implements CDataPresenter {@overrideIModel createModel() {return MData();}@overrideloadData(Map<String, dynamic> p) async{// TODO: implement loadDataview?.startLoading();return model?.loadData(p, (data) {view?.showLoadSuccess();view?.loadData(data);model?.dispose();}, (error) {view?.showLoadFailure(error.code!, error.message!);});}
}

视图层(View) 实现

class MyHomePage extends BaseView {@overrideBaseViewState<IPresenter<IView>, BaseView> getState() {// TODO: implement getStatereturn _MyHomePageState();}
}class _MyHomePageState extends BaseViewState<PData, MyHomePage>implements CDataView {var _futBuiHom;void _handleGetNetData() async {await _getNetData();setState(() {});}_getNetData() async {return await presenter!.loadData({'ie': 'utf-8', 'kw': '大佬', 'fr': 'search'});}@overridevoid initState() {// TODO: implement initStatesuper.initState();_futBuiHom = _getNetData();}@overrideloadData(d) {}@overridePData? createPresenter() {// TODO: implement createPresenterreturn PData();}@overrideColor buildBodyColor() {// TODO: implement buildBodyColorreturn Color(0xffFFFFFF);}@overridebuildWidget() {// TODO: implement buildWidgetprint('build(BuildContext context)');return Column(children: [AppBar(title: Text('FutureBuilder MVP Dio'),),OutlinedButton(onPressed: _handleGetNetData,child: Text('模拟从网络获取数据'),),Expanded(flex: 1,child: Center(child: FutureBuilder<dynamic>(future: _futBuiHom,initialData: '初始化的数据',builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {print('\n获取到到数据: State${snapshot.connectionState} \n data${snapshot.data}');switch (snapshot.connectionState) {case ConnectionState.none:case ConnectionState.waiting:return const Text('数据加载中.....'); //加载中default: //如果_calculation执行完毕if (snapshot.hasError) {//若_calculation执行出现异常return Text('Error: ${snapshot.hasError}');} else {//若_calculation执行正常完成return Text('Error: ${snapshot.data}');}}},),),),],);}
}

main() 函数初始化网络请求 (网络请求需要的域名baseUrl)

void main() async {WidgetsFlutterBinding.ensureInitialized();await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);await SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);///网络请求管理初始化HttpManager().init(baseUrl: 'https://tieba.baidu.com');runApp(MyApp());
}

发起了网络请求

   

总结 

FutureBuilder 用于显示异步请求的数据, 避免数据请求多次调用builder 对应的函数 , 我们需要在Widget 创建时 State 函数 initState 里面进行异步请求的初始化 .

Future+Dio+MVP 案例下载

Flutter 项目实战(Dio+MVP+FutureBuilder )五相关推荐

  1. Flutter 项目实战 Dio网络请求 四

    /  HTTP  |   HTTPS  / HTTP是一个客户端(用户)和 服务端(网站)之间请求和应答的标准,通常使用TCP协议.客户端发起一个HTTP请求到服务器上指定端口(默认端口为80).客户 ...

  2. 12、Flutter - 项目实战 - 仿微信(六)聊天页面

    Flutter - 项目实战 - 仿微信(六)聊天页面 接上篇:11.Flutter - 项目实战 - 仿微信(五)通讯录 详细代码参见Demo Demo地址 -> wechat_demo 其他 ...

  3. flutter 项目实战二 网络请求

    本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用. 逛丢官方网址:https://guangdiu.com/ flutter windows开发环境设置 flutter 项目实战 ...

  4. flutter 项目实战四 列表数据展示

    本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用. 逛丢官方网址:https://guangdiu.com/ flutter windows开发环境设置 flutter 项目实战 ...

  5. Flutter项目实战之女装商城------火爆专区实现、商品分类数据准备

    火爆专区实现: 继续Flutter项目的学习,在上一次https://www.cnblogs.com/webor2006/p/14410445.html咱们已经对于首页的大部分功能已经完成,但是呢,还 ...

  6. Flutter 项目实战 滚动悬浮导航栏 十五

  7. Flutter 项目实战 架构模式四

    / 你所理解的架构 / 从古到今,无论是修建高大的建筑还是几层或者几十层的房屋都需要进行一个架构,正所谓万盏高楼平地起,一切靠架构.团队的管理从基层员工到高层都需要一个体系架构图,这样方便公司更好的运 ...

  8. Flutter 项目实战 拍照 | 打开相册 | 上传图片 八

    /  Ios . Android 应用权限开启流程 / IOS 应用 (询问权限.开启权限) Android 应用(询问权限.开启权限) / 自定义选择相机和相册的对话框 / 创建一个存放对话框标题. ...

  9. Flutter项目实战教程分享、基础使用、性能优化、每日积累

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,优美的应用体验 来自于细节的处理,更源自于码农的自我要求与努力 Flutter是谷歌推出的最新的移动开发框架,本文章为横向经验积累分享目 ...

最新文章

  1. 《Linux内核设计与实现》读书笔记 第三章 进程管理
  2. PHP利用Gearman来处理并行多进程问题
  3. 现在python已经更新到哪个版本了-Python 3.8 已发布 你会升级么?
  4. nodejs库express是如何接收inbound json请求的
  5. 如何在Java中将String转换为int
  6. 团队项目计划BACKLOG
  7. study notes for python
  8. jsp mysql登录 demo_java jsp+servlet+mysql实现登录网页设计
  9. 从今天起,TensorFlow 1.9开始支持树莓派了
  10. HTTP 错误 404.2 - Not Found 由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面。...
  11. 代码大全旁边的一本书--感受《UNIX编程艺术》
  12. 面试题:谈谈如何优化MYSQL数据库查询
  13. 万卷书计划-2016年开启
  14. excel打开密码忘记了_忘记EXCEL表格密码怎么办,这样操作可以清除
  15. Cadence: 各软件业务
  16. 【Unity3D】图片纹理压缩方式,干货走起!
  17. Oracle Net Services 配置失败。退出代码是1 参数responsefile = Oracle Net Configuration Assistant 在给定位置找不到响应文件。
  18. 故宫景点功课24:宁寿宫区6
  19. 微信小程序+云开发项目实战:商品类小程序(化妆品门店)
  20. UIswitch 的用法

热门文章

  1. 人生的追求到底是什么?
  2. 【转载】Camera安卓源码-高通mm_camera架构剖析
  3. 安卓基础学习之handler
  4. 废品回收小程序搭建是如何实现运营方案整合以及利润的产生
  5. 论文阅读笔记(一)——铁饼鱼的面部识别:使用数字模型的实验方法
  6. php e all e notic,PHP error_reporting(E_ALL ^ E_NOTICE)一些资料整理
  7. 【软件质量】软件互操作性
  8. N诺刷题(基础算法)
  9. Python3:基于Opencv4.2和wxPython4可视化界面的人脸考勤系统
  10. NTC 10k-25°C