flutter 多环境配置及Dio请求封装最佳实践
- flutter 多环境配置
- 项目结构概览
- 创建文件
- 环境变量公共配置文件 config.dart
- 配置 dev 环境 (开发环境)
- 配置 prod 坏境(生产环境)
- 调整项目入口文件 main.dart
- 设置项目主页 index.dart
- 配置 android studio 启动命令
- 封装 Dio 请求
- 创建 http.dart 文件
- 创建 interface.dart 文件
- 调用接口
- 最后我们启动项目看下效果吧
- 模板项目地址
- 结束语
- 如果这篇文章对你有帮助请支持下作者创作更多最佳实践文章
- 支付宝
- 微信
小伙伴,很高兴能看到这篇文章,也许你是 flutter 大牛,也许你是小白,请耐心看完, 这是 flutter 多环境配置及 Dio 请求封装的最佳实践, 相信这也是全网最好的一篇 flutter 实践文章, 适合大中小型项目, 相信一定会有所收获
如果阅读过程中让你感到困惑,请按照文章步骤,创建对应文件,编写代码运行项目感受一下,相信这不会让你失望的
flutter 多环境配置
1 2 3 | 所谓多环境是指 开发环境, 联调环境, 测试环境, 演示环境, 生产环境等,当我们在各个环境之间切换时,势必需要快速高效的解决方 案, 多环境配置由此而生, 相信在看到这篇文章之前,也看过很多关于环境配置的博文,大多都是在项目中手动切换环境变量以达到环境 切换目的, 虽然能解决问题但是总感觉有些low,下面让我解开 flutter 正确的启动方式 |
项目结构概览
1 2 | 先来看下图的 项目结构总览, 接下来我们会逐一创建各个文件,并详细讲解其作用 ==需要注意每个文件的位置== |
- config.dart // 环境配置文件
- dev.dart // 开发环境配置文件
- prod.dart // 生产环境配置文件
- main.dart // 项目入口文件
- index.dart // 项目主页文件
- http.dart // dio请求封装文件
- interface.dart // 接口文件
创建文件
-
在 lib 目录下创建 public 文件夹
-
在public目录下创建 config.dart 文件
-
在public目录下创建 dev.dart 文件
-
在public目录下创建 prod.dart 文件
目前先创建这几个文件
环境变量公共配置文件 config.dart
1 | 打开 config.dart 文件 编写如下代码 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /// 注意这里包的引入路径 /// 推荐使用 android studio 编写 flutter 应用 /// 如果你是 Android studio 用户 这里可以不着急引入包,当你把代码复制到自己的项目里时,由于路径不对, /// 编辑器检测不到对应的包时,对应的代码会变红,如下图,此时把选中红色波浪线的单词按住 ctrl + enter键 选择需要的包就好了 import 'package:flutter/material.dart'; /** * @CreateDate: 2020/7/2 20:45 * @Author: Gleason * @Description: 环境变量 **/ /// 这里继承自 InheritedWidget 这个类 /// 可以在 weiget 中通过上下文获取到环境变量 /// 当然 也可把 主题和一些全局 设置放到这里 class ENV extends InheritedWidget { static String appName; // 系统名称 static String envName; // 运行环境 static String baseUrl; // 基础url ENV({ @required String appName, @required String envName, @required String baseUrl, @required Widget child, }) : super(child: child){ ENV.appName = appName; ENV.envName = envName; ENV.baseUrl = baseUrl; } // 这里 是在 weiget 中拿到当前环境变量的关键 static ENV of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(aspect: ENV); } @override bool updateShouldNotify(InheritedWidget oldWidget) => false; } |
配置 dev 环境 (开发环境)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import 'package:flutter_template/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_template/public/config.dart'; void main() { var configuredApp = new ENV( appName: 'app 名称', // 项目名称 或者 app 名称 envName: 'dev', // 环境变量 baseUrl: 'https://www.dev.com/', // 接口基础地址 child: new MyApp(), ); runApp(configuredApp); } |
配置 prod 坏境(生产环境)
1 2 3 4 5 6 7 8 9 10 11 12 13 | import 'package:flutter_template/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_template/public/config.dart'; void main() { var configuredApp = new ENV( appName: 'app 名称', // 项目名称 或者 app 名称 envName: 'prod',// 环境变量 baseUrl: 'https://www.prod.com/', // 接口基础地址 child: new MyApp(), ); runApp(configuredApp); } |
调整项目入口文件 main.dart
1 2 | 如果不知道 main.dart 文件那来的 请看开篇 项目结构概览 因为 main.dart 作为项目入口文件 所以我们要把项目首页进行迁移到 index.dart 做下功能文件区分 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /// 项目入口文件 import 'package:flutter/material.dart'; import 'package:flutter_template/index.dart'; import 'package:flutter_template/public/config.dart'; class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 在任何地方调用AppConfig.of(context)以获取特定于环境的配置 var config = ENV.of(context); print('config$config') return new MaterialApp( title: ENV.appName, theme: new ThemeData( primarySwatch: Colors.blue, ), home: new HomePage(), //这里将引入 index.dart 中的 homepage weiget ); } } |
设置项目主页 index.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import 'package:flutter/material.dart'; /// 这里需要引入 环境变量配置 import 'package:flutter_template/public/config.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => new _HomePageState(); } class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(ENV.appName), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Container( /// 通过 ENV 环境变量可以拿到我们配置的环境信息 child: Text('app 名称: ${ENV.envName}.'), padding: EdgeInsets.all(15.0), ), Container( child: Text('baseUrl: ${ENV.baseUrl}.'), padding: EdgeInsets.all(15.0), ), ], ), ), ); } } |
配置 android studio 启动命令
1 | 按照图示选择 Edit configur... 选项, 接下来按照 下面图片跟着配置就可以了,如果不知道我在干啥,就先跟着做,一会配置完就懂了 |
1 | 重复上述操作,照此方式配置 prod 测试, 演示, 联调环境吧 |
相信照着上面配置完 你的 main.dart 被打了一个小红叉了吧
接下来 删除 原来 main.dart 启动命令
现在你的启动项应该和我的一样了, 点击 启动项 旁边的启动按钮试试把 是不是感觉打开了新世界的大门
封装 Dio 请求
1 | 接下来我们封装的 dio 请求要和之前的 环节配置关联上这样才是一个整体, 跟着往下操作吧 |
创建 http.dart 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter_template/public/config.dart'; /// dio 初始化设置 final dio = Dio(BaseOptions( baseUrl: ENV.baseUrl, // 如果你启动的是 dev 那么这里的 baseUrl = https://www.dev.com/ connectTimeout: 5000, // 配置请求超时 receiveTimeout: 100000, contentType:"application/json; charset=utf-8", // 设置content-type )); /// 接口封装 /// 说到这里我们先来讲下接口构成 /// 接口 = 基础路径 + 接口地址 也就是 interface = baseUrl + path /// 比如说登录接口 http://www.dev.com/login /// 这个登录接口的构成 由 baseUtl(http://www.dev.com/) 和 path(login) 组成 /// baseUrl 我们在环境变量 设置过(dev 和 prod文件里的baseUrl) /// path 是我们在后端提供的接口文档中获得的 /// 下面 /// main 方法中的参数 url 只需传 login 即可 /// main 方法中 type 是请求类型 如get/post/delete/put .... /// body 是请求带参 Future main({String url = '', String type = "get", dynamic body}) async { type = type.toUpperCase(); print('请求参数: url:$url,type:$type,body:$body'); if (type == 'POST') { // await dio.(url).then((value) => value).catchError((e)=>e); Response response = await dio.post(url,data: body); return response.data; } } |
创建 interface.dart 文件
1 2 3 4 5 6 7 8 9 10 11 | import 'package:flutter_template/public/http.dart'; class Fetch { /// login 接口 static login(body)=> main(url: 'login',type: 'post', body:body); /// 注册 接口 static register(body)=> main(url: 'register',type: 'post', body:body); /// 把项目中用到 所有接口都写在这里 接口统一管理 /// 如果接口名称改动了 只需要 修改 url 参数即可,项目里用到该接口的地方就不需要修改了 /// 小伙伴是不是觉得 这才是 flutter 最佳实践 } |
调用接口
1 | 接下来我们要结合上边所有配置 调用我们配置好的接口了 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import 'package:flutter/material.dart'; import 'package:flutter_template/public/config.dart'; import 'package:flutter_template/public/interface.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => new _HomePageState(); } class _HomePageState extends State<HomePage> { /// 接收 调用接口返回数据变量 String data; /// 组件初始化时 调用接口 @override void initState() { loadData(); super.initState(); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(ENV.appName), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Container( child: Text('app 名称: ${ENV.envName}.'), padding: EdgeInsets.all(15.0), ), Container( child: Text('当前环境: ${ENV.baseUrl}.'), padding: EdgeInsets.all(15.0), ), Container( child: Text('响应数据: \n$data.'), padding: EdgeInsets.all(15.0), ), ], ), ), ); } /// 调用接口方法 void loadData() async { Map result = await Fetch.login({'userid': 'bianliuzhu', 'password': 'bianliuzhu'}); print('result:${result}'); /// 更新数据 setState(() { data = result['user'].toString(); }); } } |
最后我们启动项目看下效果吧
模板项目地址
有些小伙伴可能脑袋不够用了, 或者编写代码时出错了,这里我放了自己的模板项目地址
链接: 模板项目地址.
结束语
1 2 | 到这里我们所有的配置都已经完成了, 另外根据请求方法不同和后台返回数据格式不同, 在 http.dart 文件中我只配置了 post 请求的, 只对我们公司的数据进行了格式处理, 简单举个列子,小伙伴们需要举一反三,自己配置其他请求和数据格式化 |