美文网首页flutter
基于GetX 搭建通用flutter 项目《二》(界面规范抽象类

基于GetX 搭建通用flutter 项目《二》(界面规范抽象类

作者: 火之夜工作室 | 来源:发表于2022-07-08 22:59 被阅读0次

    最近由于一直在写小程序开发,也是没有太多的时间,就耽误了这个文章的更新,实在是不好意思,好了,废话不多说了,直接上主题
    基于GetX 搭建通用flutter 项目《一》

    您能在这里看到啥

    1. 抽象类
    2. 通用属性抽象类
    3. 通用界面抽象类
    4. 通用列表页抽象类
    5. 通用列表页刷新事件抽象类
    6. 网络事件抽象类

    这些抽象类,是在自己的项目中,不断试错,自己总结的一些让自己偷懒的方法,一直以来,我对自己的写代码的要求就是,能“偷懒“就“偷懒“,减少代码中重复且可以抽象的方法的出现。喜欢折腾,会把之前写过的东西,不断的换着不同的方式,方法,去重写,在不断的重写中,慢慢的积累,不断的提升自己的思维能力,让自己变得更“懒”。

    送给自己一句话

    慢就是快

    抽象类介绍

    通用属性抽象类

    abstract class AbstractAttribute {
        /// 导航栏标题
        final String? title = null;
        /// 导航栏颜色
        final Color? navbackcolor = null;
        /// 默认安全区顶部 忽略
        final bool safeAreatop = true;
        /// 默认安全区底部 忽略
        final bool safeAreabottm = true;
        /// 网络加载状态
        final PageState pageState = PageState.initializedState;
        /// 背景颜色
        final Color? backgroundColor = null;
        /// 滚动属性
        final bool? resizeToAvoidBottomInset = null;
    }
    

    从代码里,我们会发现,这里面只有一个属性非系统自带的,那就是PageState,其实这是我自己定义的界面显示的状态值,这里面包含了网络请求的状态值,以及界面初始化,加载完成等等,往👇看

    enum PageState {
        // 初始状态
        initializedState,
        // 错误状态,显示失败界面
        errorState,
        // 错误状态,只弹错误信息
        erroronlyTotal,
        // 错误状态,显示刷新按钮
        errorshowRelesh,
        // 没有更多数据
        noMoreDataState,
        // 空数据状态
        emptyDataState,
        // 数据获取成功状态
        dataFetchState,
    }
    

    相对来说,这是我一般项目中,界面的一般状态值,这是我自己的总结,不代表,只能这样写,这个是需要根据您的业务场景,自己去归纳总结,灵活使用就行,主要是要学会,把界面通用的事件,和状态,抽离出来。方便使用者使用,且要简单易懂。不能只能你自己知道,这样的抽离,是没有意义。万物归一,简单至上。具体这个属性和状态值的是使用,我会在基于GetX 搭建通用flutter 项目《五》,这边文章里,详细介绍。
    当然,有了属性,必然会有使用的地方,下面我们就来看看这些属性,到底能做些什么呢,快看界面抽象类

    通用界面抽象类

    这里会相对来说多一些,请您慢慢看,上代码👇

    // 普通界面 规范
    
    abstract class AbstractWidget {
        /// 创建scaffoll
        Widget createScaffol({
            required BuildContext context,
            required bool safeAreatop,
            required bool safeAreabottm,
            required PageState pageState,
            required String? title,
            required Color? backgroundColor,
            required Color? navbackcolor,
            required bool? resizeToAvoidBottomInset,
        }) {
            return Scaffold(
                resizeToAvoidBottomInset: resizeToAvoidBottomInset,
                backgroundColor: backgroundColor,
                appBar: createAppBar(
                    context,
                    title: title,
                    navbackcolor: navbackcolor,
                ),
                body: createSafeArea(
                    context,
                    safeAreatop,
                    safeAreabottm,
                    pageState,
                ),
            );
        }
        /// 创建safe
        Widget createSafeArea(
            BuildContext context,
            bool safeAreatop,
            bool safeAreabottm,
            PageState pageState,
        ) {
            return SafeArea(
                child: createCommBaseWidget(
                    context,
                    pageState,
                ),
                top: safeAreatop,
                bottom: safeAreabottm,
            );
        }
        /// 创建导航栏
        PreferredSize? createAppBar(
            BuildContext context, {
            Color? navbackcolor,
            String? title,
        }) {
            if (title != null) {
                return AppBarGenerator.getNoramlAppBar(
                    context: context,
                    title: title,
                    backgroundColor: navbackcolor,
                    actions: createAppBaractions(),
                    leading: createAppBarleading(),
                    textColor: createAppBarTextColor(),
                    leadingIconColor: createLeadingIconColor(),
                    leadingCallback: () {
                    configleadingCallbak(context);
                    },
                    titlew: createAppBarTitleWidget()
                );
            }
            return null;
        }
        /// 创建AppBar titleWidget
        Widget? createAppBarTitleWidget() {
            return null;
        }
        /// 设置系统自带的返回按钮颜色
        Color? createLeadingIconColor() {
            return null;
        }
        /// 设置AppBar text 字体颜色
        Color? createAppBarTextColor() {
            return null;
        }
        /// 创建导航栏 右边按钮集合
        List<Widget>? createAppBaractions() {
            return null;
        }
        /// 重写返回按钮控件
        Widget? createAppBarleading() {
            return null;
        }
        /// 创建通用站位界面
        Widget createCommBaseWidget(
            BuildContext context,
            PageState pageState,
        ) {
            /// CommonBasePage 是我自己定义的通用界面暂位页。
            return CommonBasePage(
                pageState: pageState,
                child: createColumWidget(context),
                errorWidget: createErrorWidget(),
            );
        }
        /// 创建界面Colum
        Widget createColumWidget(BuildContext context) {
            return Column(
                children: [
                    createHeaderWidget(),
                    Expanded(
                        child: createBody(context),
                    ),
                ],
            );
        }
        /// 创建头部
        Widget createHeaderWidget() {
            return Container();
        }
        /// 创建真实body
        @protected
        Widget createBody(BuildContext context);
        /// 创建失败 界面
        Widget? createErrorWidget() {
            return null;
        }
        /// 点击通用返回按钮点击事件
        configleadingCallbak(BuildContext context) {
            configgoback(context);
        }
        /// 执行返回界面
        configgoback(BuildContext context) {
            Navigator.of(context).pop();
        }
    
    }
    
    
    • Widget createScaffol() 创建界面入口方法

      下面是函数需要的参数

        /// 上下文,这就不再讲了
        required BuildContext context,
        /// 是否关闭顶部安全区域,这是当你需要沉浸式,和顶部置顶的时候,需要设置
        required bool safeAreatop,
        /// 是否关闭底部安全取悦,一般我都用于列表类界面。
        required bool safeAreabottm,
        /// 界面状态值
        required PageState pageState,
        /// 导航栏标题。我这边做的处理是,当title == null 就没有导航栏
        required String? title,
        /// 背景颜色
        required Color? backgroundColor,
        /// 导航栏背景颜色
        required Color? navbackcolor,
        /// 键盘启动 界面弹性 配置
        required bool? resizeToAvoidBottomInset,
    
    • PreferredSize? createAppBar() 创建导航栏
      从👆的代码里,我们很快就找到了创建导航栏 的方法,为了方便在使用的时候,更好的自定义导航栏,我在自己的使用过程中,增加了几个可以自定义导航栏的方法,👇,当然,如果你不喜欢我定义的,您也可以直接重写这个方法。来到达您的预期。

       /// 自定义默认返回Icon 的颜色,我这里默认用的 Icons.arrow_back_ios
       - Color? createLeadingIconColor()
       /// 自定义导航栏字体颜色
       - Color? createAppBarTextColor()
       /// 创建导航栏👉按钮集合
       - List<Widget>? createAppBaractions()
       /// 重写返回按钮控价
       Widget? createAppBarleading()
      

      其实这些方法,也是可以在通用属性抽象类 添加属性,由于这里为了体现两种替换方式,我这边,把只属于appBar的部分自定义属性,以重写方法的形式,来达到替换默认属性的效果,当然在重写通用属性抽象类中的属性的时候,我推荐👇的方式来重写

      
       /// 第一种方式,直接重写get 方法,返回默认值
       @override
       Color? get backgroundColor => Colors.white;
       /// 第二种方式,也是重写get 方法,但获取的可能是动态变更的,两种方式
       /// 都是一样效果,不过是不是觉得第二种。和上面的定义抽象接口,再重写,
       /// 其实是一样的
       /// 不管哪种方式,都只是一种方式,最终是我们形成体系,大家都按照我们规定好的方式
       /// 来进行开发。节省沟通成本
       @override
       Color? get navbackcolor => configbackColor();
       Color? configbackColor() {
           return null;
       }
      
    • Widget createSafeArea() 创建安全区域body
      这里面主要做了一件事,就是我们可视界面都被SafeArea 包裹着,然后我们的child的载体就是我们封装的展位界面,这个暂位界面会根据,你设定的界面状态值pagestate 进行了逻辑处理,到底界面是显示我们的空数据界面网络请求失败,还是业务界面

    /// 创建通用站位界面
    /// 这里面就需要您来传递,界面的状态值,
    /// 有了状态值,站位界面,才能根据事先约定好的状态,来完成界面显示
    Widget createCommBaseWidget(
        BuildContext context,
        /// 界面状态,可以查看👆
        /// 我的状态值的定义
        PageState pageState,
    ) {
        /// 这里就是我封装的 站位界面的 工具
        return CommonBasePage(
            pageState: pageState,
            child: createColumWidget(context),
            /// 为了方便扩展,我在这里对外公开了一个方法
            /// 用于创建你自己的失败界面.
            errorWidget: createErrorWidget(),
        );
    }
    
    • Widget createColumWidget(BuildContext context) 创建界面分成
    /// 创建界面Colum
    Widget createColumWidget(BuildContext context) {
        return Column(
            children: [
                createHeaderWidget(),
                Expanded(
                    child: createBody(context),
                ),
            ],
        );
    }
    
    

    我这里用的是column,因为大部分界面可以分为

        /// header 可以通过👆的方法,自定header
        /// 一般这个用的不多
        header (头部)
        /// 这里才是我们真正内容的显示
        /// 在这里,我一般是需要使用者,必须实现这个方法的
        body(内容)
    

    好了到这里,这个通用的界面抽象类暂时完成了,由于我们的项目开发中还有有很多组合,我这里也把通用列表,弄了一个简单抽象类请看👇

    通用列表抽象类

    /// 刷新界面 规范
    abstract class AbstracRefreshWidget {
        /// 创建刷新控件
        Widget createRefreshWidget(BuildContext context);
        /// 创建列表
        Widget createListView(BuildContext context);
        /// 创建列表 item
        Widget createListitem(BuildContext context, int index);
        /// 创建缺省页界面
        Widget? createEmptyWidget();
    }
    

    相对来说,这里只是抽象了界面的方法,并没有包含事件的方法,我这么做的目的,是为了让我们的抽象类,
    更纯粹一些,界面就是界面,事件就是事件.也方便使用MVC.或者MVVM,既然有了界面抽象类,自然也少不了
    我们的方法抽象类,请往下看

    通用列表刷新事件抽象类

    /// 刷新界面 触发方法
    
    abstract class AbstracRefreshMehod {
        int page = 1;
        /// 结束刷新
        void endRefresh(int type, PageState pageState);
        /// 下啦刷新 触发事件
        void configRefresh();
        /// 上啦加载 触发事件
        void configLoading();
    }
    

    这里面的代码就相对简单了,就不再啰嗦了,当然下面的网络,我也就不再介绍了,相对来说比较简单

    网络事件抽象类

    // 配置网络请求规范
    
    abstract class AbstractNetWork {
        @protected
        /// 通用网络参数
        Map<String, dynamic>? configNetWorkParmas({int? type});
        /// 网络请求
        @protected
        void getnetworkdata(int? type, Map<String, dynamic>? info);
    }
    

    具体使用事例

    state 使用事例

    第一步,创建适用你项目的抽象类.主要就是把我们上面定义的抽象类,进行一个组合.

    StatefulWidget的抽象类

    /// 其实这个到没有太有必要做成抽象类.
    abstract class NormalStatefulWidget extends StatefulWidget {
        const TTNormalStatefulWidget({Key? key}) : super(key: key);
        @override
        // ignore: no_logic_in_create_state
        TTNormalState createState() => getState();
        TTNormalState getState();
    }
    
    
    

    State 抽象类

    
    abstract class NormalState<T extends StatefulWidget> extends State<T>
    with AbstractNetWork, AbstractWidget, AbstractAttribute {
    /// 生命周期
    ///
    ///
    
    /// 界面初始化完成
    @override
    void initState() {
        initDefaultState();
        super.initState();
    }
    /// 界面构建视图入口
    @override
    Widget build(BuildContext context) {
        return createScaffol(
        context: context,
        safeAreatop: safeAreatop,
        safeAreabottm: safeAreabottm,
        pageState: pageState,
        resizeToAvoidBottomInset: resizeToAvoidBottomInset,
        title: title,
        backgroundColor: backgroundColor,
        navbackcolor: navbackcolor,
        );
    }
    @override
    void dispose() {
        initDefaultDispose();
        super.dispose();
    }
    @override
    Widget createColumWidget(BuildContext context) {
        return Container(
        alignment: Alignment.center,
        child: LayoutBuilder(
        builder: (context, constraints) {
        configlayoutbuiderConstraints(constraints);
        return SizedBox(
        width: configSizeBoxWidth(constraints),
            child: createonlyColumWidget(context));
        },
        ),
        );
    }
    /// getx 真实包裹的colum 方法
    
    Widget createonlyColumWidget(BuildContext context) {
        return Column(
            children: [
                createHeaderWidget(),
                Expanded(
                    child: createBody(context),
                ),
            ],
        );
    }
    /// 网络请求
    ///
    ///
    @override
    void getnetworkdata(int? type, Map<String, dynamic>? info) {}
    /// 创建视图
    ///
    ///
    
    /// 触发方法
    ///
    ///
    @override
    Map<String, dynamic>? configNetWorkParmas() {
        return null;
    }
    /// 界面进入
    void initDefaultState() {}
    /// 界面销毁
    void initDefaultDispose() {}
    /// 通用刷新state
    void configsetState(VoidCallback fn) {
        if (mounted) {
            setState(() {
                fn();
             });
        }
    }
    /// 获取 屏幕 最大尺寸
    
    configlayoutbuiderConstraints(BoxConstraints constraints) {}
    double? configSizeBoxWidth(BoxConstraints constraints) {
        return HzyNormalUtils.configSizeMaxW(constraints.maxWidth);
    }
    }
    

    好了,今天就写到这里了,喜欢的可以点个赞
    对应的基于GetX 封装的将会在
    [基于GetX 搭建通用flutter 项目《五》()这个文章里讲解

    hzy_normal_widget 是我在使用GetX搭建项目时,总结的一些通用开发控件,方便我们在开发的时候,减少重复性界面代码的创建.

    ttcomment 通用项目的界面接口基类,和一些通用工具类,喜欢的可以点点star.

    相关学习资料

    相关文章

      网友评论

        本文标题:基于GetX 搭建通用flutter 项目《二》(界面规范抽象类

        本文链接:https://www.haomeiwen.com/subject/zkcbbrtx.html