Flutter项目开发模版,开箱即用

前言

当前案例 Flutter SDK版本:3.22.2

每当我们开始一个新项目,都会 引入常用库、封装工具类,配置环境等等,我参考了一些文档,将这些内容整合、简单修改、二次封装,得到了一个开箱即用的Flutter开发模版,即使看不懂封装的工具对象原理,也没关系,模版化的使用方式,小白也可以快速开发Flutter项目。

快速上手

用到的依赖库

  dio: ^5.4.3+1 // 网络请求
  fluro: ^2.0.5 // 路由
  pull_to_refresh: ^2.0.0 // 下拉刷新 / 上拉加载更多
  sentry_flutter: ^7.8.0 // 异常上报

修改规则

默认使用的是Flutter团队制定的规则,但每个开发团队规则都不一样,违反规则的地方会出现黄色波浪下划线,比如我定义常量喜欢字母全部大写,这和默认规则不符;

修改 Flutter项目里的 analysis_options.yaml 文件,找到 rules,添加以下配置;

  rules:
    use_key_in_widget_constructors: false
    prefer_const_constructors: false
    package_names: null

 修改前

修改后 

MVVM

  • MVVM 设计模式,相信大家应该不陌生,这里并不是传统的MVVM,我魔改了,简单说一下每层主要负责做什么;
  • Model: 数据相关操作;
  • View:UI相关操作;
  • ViewModel:业务逻辑相关操作。

持有关系:

View持有 ViewModel;

Model持有ViewModel;

ViewModel持有View;

ViewModel持有Model;

注意:这种持有关系,有很高的内存泄漏风险,所以我在基类的 dispose() 中进行了销毁子类重写一定要调用 super.dispose()

/// BaseStatefulPageState的子类,重写 dispose()
/// 一定要执行父类 dispose(),防止内存泄漏
@override
void dispose() {
  /// 销毁顺序

  /// 1、Model 销毁其持有的 ViewModel
  if(viewModel?.pageDataModel?.data is BaseModel?) {
    BaseModel? baseModel = viewModel?.pageDataModel?.data as BaseModel?;
    baseModel?.onDispose();
  }
  if(viewModel?.pageDataModel?.data is BasePagingModel?) {
    BasePagingModel? basePagingModel = viewModel?.pageDataModel?.data as BasePagingModel?;
    basePagingModel?.onDispose();
  }

  /// 2、ViewModel 销毁其持有的 View
  /// 3、ViewModel 销毁其持有的 Model
  viewModel?.onDispose();

  /// 4、View 销毁其持有的 ViewModel
  viewModel = null;

  /// 5、销毁监听App生命周期方法
  lifecycleListener?.dispose();
  super.dispose();
}

基类放在文章最后说,这里先忽略;

Model

class HomeListModel extends BaseModel {

    ... ... 

	ValueNotifier<int> tapNum = ValueNotifier<int>(0); // 点击次数

	@override
    void onDispose() {
	  tapNum.dispose();
      super.onDispose();
    }

    ... ...
	
}

... ...

View

class HomeView extends BaseStatefulPage<HomeViewModel> {
  HomeView({super.key});

  @override
  HomeViewState createState() => HomeViewState();
}

class HomeViewState extends BaseStatefulPageState<HomeView, HomeViewModel> {

  @override
  HomeViewModel viewBindingViewModel() {

    /// ViewModel 和 View 相互持有
    return HomeViewModel()..viewState = this;

  }

  /// 初始化 页面 属性
  @override
  void initAttribute() {
    ... ...
  }

  /// 初始化 页面 相关对象绑定
  @override
  void initObserver() {
    ... ...
  }

  @override
  void dispose() {
    ... ... 

    /// BaseStatefulPageState的子类,重写 dispose()
    /// 一定要执行父类 dispose(),防止内存泄漏
    super.dispose();
  }

  ValueNotifier<int> tapNum = ValueNotifier<int>(0);

  @override
  Widget appBuild(BuildContext context) {

    ... ...

  }

  /// 是否保存页面状态
  @override
  bool get wantKeepAlive => true;

}

ViewModel

class HomeViewModel extends PageViewModel<HomeViewState> {

  @override
  onCreate() {
  
    /// 拿到 页面状态里的 对象、属性 等等
    debugPrint('---runSwitchLogin:${state.runSwitchLogin}');
    ... ... 

    /// 初始化 网络请求
    requestData();
  }

  @override
  onDispose() {
    ... ...

    /// 别忘了执行父类的 onDispose
    super.onDispose();
  }

  /// 请求数据
  @override
  Future<PageViewModel?> requestData({Map<String, dynamic>? params}) async {
    
    ... ...

  }
}

网络请求

BaseRepository

typedef JsonCoverEntity<T extends BaseModel> = T Function(Map<String, dynamic> json);

class BaseRepository {

  /// 统一处理 响应数据,可以避免写 重复代码,但如果业务复杂,可能还是需要在原始写法上,扩展

  /// 普通页面(非分页)数据请求 统一处理
  Future<PageViewModel> httpPageRequest({
    required PageViewModel pageViewModel,
    required Future<Response> future,
    required JsonCoverEntity jsonCoverEntity,
    CancelToken? cancelToken,
    int curPage = 1,
  }) async {
    try {
      Response response = await future;

      if (response.statusCode == REQUEST_SUCCESS) {
        /// 请求成功
        pageViewModel.pageDataModel?.type = NotifierResultType.success;

        /// ViewModel 和 Model 相互持有
        dynamic model = jsonCoverEntity(response.data);
        model.vm = pageViewModel;
        pageViewModel.pageDataModel?.data = model;
      } else {
        /// 请求成功,但业务不通过,比如没有权限
        pageViewModel.pageDataModel?.type = NotifierResultType.unauthorized;
        pageViewModel.pageDataModel?.errorMsg = response.statusMessage;
      }
    } on DioException catch (dioEx) {
      /// 请求异常
      pageViewModel.pageDataModel?.type = NotifierResultType.dioError;
      pageViewModel.pageDataModel?.errorMsg = dioErrorConversionText(dioEx);
    } catch (e) {
      /// 未知异常
      pageViewModel.pageDataModel?.type = NotifierResultType.fail;
      pageViewModel.pageDataModel?.errorMsg = e.toString();
    }

    return pageViewModel;
  }

  /// 分页数据请求 统一处理
  Future<PageViewModel> httpPagingRequest({
    required PageViewModel pageViewModel,
    required Future<Response> future,
    required JsonCoverEntity jsonCoverEntity,
    CancelToken? cancelToken,
    int curPage = 1,
  }) async {
    try {
      Response response = await future;

      if (response.statusCode == REQUEST_SUCCESS) {
        /// 请求成功
        pageViewModel.pageDataModel?.type = NotifierResultType.success;

        /// 有分页
        pageViewModel.pageDataModel?.isPaging = true;

        /// 分页代码
        /// ViewModel 和 Model 相互持有代码,写着 correlationPaging() 里面
        pageViewModel.pageDataModel?.correlationPaging(
            pageViewModel, 
            jsonCoverEntity(response.data) as dynamic, 
        );
      } else {
        /// 请求成功,但业务不通过,比如没有权限
        pageViewModel.pageDataModel?.type = NotifierResultType.unauthorized;
        pageViewModel.pageDataModel?.errorMsg = response.statusMessage;
      }
    } on DioException catch (dioEx) {
      /// 请求异常
      pageViewModel.pageDataModel?.type = NotifierResultType.dioError;
      pageViewModel.pageDataModel?.errorMsg = dioErrorConversionText(dioEx);
    } catch (e) {
      /// 未知异常
      pageViewModel.pageDataModel?.type = NotifierResultType.fail;
      pageViewModel.pageDataModel?.errorMsg = e.toString();
    }

    return pageViewModel;
  }

}

Get请求

class HomeRepository extends BaseRepository {

  /// 获取首页数据
  Future<PageViewModel> getHomeData({
    required PageViewModel pageViewModel,
    CancelToken? cancelToken,
    int curPage = 1,
  }) async =>
      httpPageRequest(
          pageViewModel: pageViewModel,
          jsonCoverEntity: HomeListModel.fromJson,
          future: DioClient().doGet('project/list/$curPage/json?cid=294', cancelToken: cancelToken),
          cancelToken: cancelToken,
          curPage: curPage);


/// 这是不使用 httpPageRequest 的原始写法,如果业务复杂,可能还是需要在原始写法上,扩展
// /// 获取首页数据
// Future<PageViewModel> getHomeData({
//   required PageViewModel pageViewModel,
//   CancelToken? cancelToken,
//   int curPage = 1,
// }) async {
//   try {
//     Response response = await DioClient().doGet('project/list/$curPage/json?cid=294', cancelToken: cancelToken);
//
//     if(response.statusCode == REQUEST_SUCCESS) {
//       /// 请求成功
//       pageViewModel.pageDataModel?.type = NotifierResultType.success;
//
//       /// ViewModel 和 Model 相互持有
//       HomeListModel model = HomeListModel.fromJson(response.data);
//       model.vm = pageViewModel;
//       pageViewModel.pageDataModel?.data = model;
//     } else {
//
//       /// 请求成功,但业务不通过,比如没有权限
//       pageViewModel.pageDataModel?.type = NotifierResultType.unauthorized;
//       pageViewModel.pageDataModel?.errorMsg = response.statusMessage;
//     }
//
//   } on DioException catch (dioEx) {
//     /// 请求异常
//     pageViewModel.pageDataModel?.type = NotifierResultType.dioError;
//     pageViewModel.pageDataModel?.errorMsg = dioErrorConversionText(dioEx);
//
//   } catch (e) {
//     /// 未知异常
//     pageViewModel.pageDataModel?.type = NotifierResultType.fail;
//     pageViewModel.pageDataModel?.errorMsg = e.toString();
//   }
//
//   return pageViewModel;
// }
}

Post请求

class PersonalRepository extends BaseRepository {

  /// 注册
  Future<PageViewModel> registerUser({
    required PageViewModel pageViewModel,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async =>
      httpPageRequest(
          pageViewModel: pageViewModel,
          cancelToken: cancelToken,
          jsonCoverEntity: UserInfoModel.fromJson,
          future: DioClient().doPost(
            'user/register',
            params: params,
            cancelToken: cancelToken,
          ));

  /// 登陆
  Future<PageViewModel> loginUser({
    required PageViewModel pageViewModel,
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
  }) async =>
      httpPageRequest(
          pageViewModel: pageViewModel,
          cancelToken: cancelToken,
          jsonCoverEntity: UserInfoModel.fromJson,
          future: DioClient().doPost(
            'user/login',
            params: params,
            cancelToken: cancelToken,
          ));

/// 这是不使用 httpPageRequest 的原始写法,如果业务复杂,可能还是需要在原始写法上,扩展
// /// 注册
// Future<PageViewModel> registerUser({
//   required PageViewModel pageViewModel,
//   Map<String, dynamic>? params,
//   CancelToken? cancelToken,
// }) async {
//
//   try {
//     Response response = await DioClient().doPost(
//       'user/register',
//       params: params,
//       cancelToken: cancelToken,
//     );
//
//     if(response.status
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值