Code前端首页关于Code前端联系我们

颤动|定义各种网络请求 Widget

terry 2年前 (2023-09-23) 阅读数 83 #移动小程序

说到监控网络请求,我们是不是首先想到FutureBuilder

FutureBuilder为我们涵盖了网络请求的各种状态。

此帖仅供参考。积极地一起讨论,你也会得到一些建议!

这就是结果。

首先服务没有运行:Flutter | 定义一个通用的多功能网络请求 Widget

可以看到所有的错误信息,

然后启用服务:Flutter | 定义一个通用的多功能网络请求 Widget

1。首先定义一个通用的网络请求

既然是网络请求,那么我们首先需要定义一个通用的网络请求方法。

每个后端API都有不同的方法,有的是RSETful,有的是我们比较熟悉的GET和POST。

这里以GET为例,API服务为GitHub - 网易云音乐Node.js API服务。

网络请求使用Dio。首先创建NetUtils.dart

启动代码:

static Dio _dio;

static void init() async {
  Directory tempDir = await getTemporaryDirectory();
  String tempPath = tempDir.path;
  CookieJar cj = PersistCookieJar(dir: tempPath);
  _dio = Dio(BaseOptions(baseUrl: 'http://127.0.0.1:3000'))
    ..interceptors.add(CookieManager(cj))
    ..interceptors.add(LogInterceptor(responseBody: true, requestBody: true));
}
复制代码

在runApp之前调用,完成初始化。

然后定义一个通用的网络请求:

static Future<Response> _get(
  BuildContext context,
  String url, {
    Map<String, dynamic> params,
  }) async {
  Loading.showLoading(context);
  try {
    return await _dio.get(url, queryParameters: params);
  } on DioError catch (e) {
    if (e.response is Map) {
      return Future.value(e.response);
    } else {
      return Future.error(0);
    }
  } finally {
    Loading.hideLoading(context);
  }
}
复制代码

这里的代码很简单。该方法需要传递三个参数:

  1. context:用于showLoading
  2. url:API地址
  3. params:网络请求 方法中参数

可以为空,我们得到❀ ,然后判断接口是否返回自定义内容。

示例:如果状态码不是2xx,但仍在返回数据,Dio将抛出DioError,我们应该捕获并处理它。

如果恢复正常数据,我们仍然会恢复。如果不是正常数据,直接抛出Future.error(0)

使用这个通用方法:

/// 新碟上架
static Future<AlbumData> getAlbumData(
  BuildContext context, {
    Map<String, dynamic> params = const {
      'offset': 0,
      'limit': 10,
    },
  }) async {
  var response = await _get(context, '/top/album', params: params);
  return AlbumData.fromJson(response.data);
}
复制代码

我们可以使用这样定义的方法,它简化了以下FutureBuilder的定义。

2。确认网络请求控制器需要的功能

在第一张图中我们可以清楚地看到,实际上有三个功能:

  1. 请求数据并显示Loading
  2. 有正常数据时恢复正常,当有是错误,返回Widget错误
  3. 如果错误是Widget,可以再次按请求

不过细心的同学也发现了问题。

我们在网络请求中添加了Loading,需要一个BuildContext。

我们都知道这个BuildContext不能在initState()方法中使用。

因此,我们必须做第一次会议

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((call) {
    _request();
  });
}
复制代码

完成对我们的应用程序的研究。

3。写全局网络请求控制

讨论全局网络请求控制,实际上是FutureBuilder的封装层。

请求数据并显示Loading

但是,这里也存在一个问题:

当我们最初定义网络请求工具类时,每个网络请求都是一个方法,而Each方法有或没有参数。

我们也知道FutureBuilder未来需要通过,那我们该怎么办呢?

而且我们在使用该控件时无法调用网络请求方法,因为网络请求中包含Loading,而那个Loading需要BuildContext

这种情况我们只能传递给(Function)方法:

typedef ValueWidgetBuilder<T> = Widget Function(
  BuildContext context,
  T value,
);


final ValueWidgetBuilder<T> builder;
final Function futureFunc;
final Map<String, dynamic> params;

CustomFutureBuilder({
  @required this.futureFunc,
  @required this.builder,
  this.params,
});
复制代码

这样我们就可以在第一帧回调中调用网络请求,一石二鸟。 :

使用该控件时无需调用方法,也避免了Loading时使用BuildContext的报错问题。

Future<T> _future;

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((call) {
    _request();
  });
}

void _request() {
  setState(() {
    if (widget.params == null)
      _future = widget.futureFunc(context);
    else
      _future = widget.futureFunc(context, params: widget.params);
  });
}
复制代码

首先我们定义未来,然后我们以第一次再次通话开始未来。

正常时返回正常数据,返回Widget错误

这就需要我们将封装的网络请求与FutureBuilder、根据网络逻辑进行连接:这可以对应多个FutureBuilder的状态:

  1. 网络请求->ConnectionState.none,
  2. 显示正在加载->ConnectionState。活动
  3. 请求结束 -> ConnectionState.done
  4. 是否有数据(true or false)->snapshot.hasData
  5. 然后抛出错误->snapshot

明白了这一点,我们就可以编写代码了:

  Widget build(BuildContext context) {
    return _future == null
        ? Container(
            alignment: Alignment.center,
            height: ScreenUtil().setWidth(200),
            child: CupertinoActivityIndicator(),
          )
        : FutureBuilder(
            future: _future,
            builder: (context, snapshot) {
              switch (snapshot.connectionState) {
                case ConnectionState.none:
                case ConnectionState.waiting:
                case ConnectionState.active:
                  return Container(
                    alignment: Alignment.center,
                    height: ScreenUtil().setWidth(200),
                    child: CupertinoActivityIndicator(),
                  );
                case ConnectionState.done:
                  if (snapshot.hasData) {
                    return widget.builder(context, snapshot.data);
                  } else if (snapshot.hasError) {
                    return NetErrorWidget(
                      callback: () {
                        _request();
                      },
                    );
                  }
              }
              return Container();
            },
          );
  }
复制代码

首先判断_future是否不存在。如果为空,则表示future未初始化。

此时我建议重新设置自定义的Loading Widget,因为这个Widget会在下次网络请求时恢复,以免混淆。

然后查看ConnectionState.done中是否有数据。如果是这样,请展示未来的Widget。

如果返回错误,则返回错误。

Widget Error 可以再次点击请求

这个逻辑很简单,我之前提到的文章中有部分解释。

FutureBuilder什么时候回来?

@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
  super.didUpdateWidget(oldWidget);
  if (oldWidget.future != widget.future) {
    if (_activeCallbackIdentity != null) {
      _unsubscribe();
      _snapshot = _snapshot.inState(ConnectionState.none);
    }
    _subscribe();
  }
}
复制代码

您可以看到,如果两个 Future 不相同,则会重复该过程。如果没有,我不会离开。

而我们在上面定义了它,由于启用了Function和Params,我们可以随时重新创建Future:

void _request() {
  setState(() {
    if (widget.params == null)
      _future = widget.futureFunc(context);
    else
      _future = widget.futureFunc(context, params: widget.params);
  });
}
复制代码

Widget的误点击事件是这样写的,It will come。 FutureBuilder,即请求

作者:Flutter Notes
链接:https://juejin.im/post/5d9b4cd3f265da5bb746495f
来源:掘金Flutter | 定义一个通用的多功能网络请求 Widget

商业印刷请联系作者获得许可。非商业转载请注明来源。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门