Advanced level I of fluent habit framework

Time:2021-12-6

The birth of fluent habit

Flutter-Habit
This article mainly explains the illustration of the fluent habit architecture and the full-text architecture, which are encapsulated in full reference to the most popular MVVM + databinding mode of Android at present. The purpose is to make the client personnel more friendly to understand the fluent habit framework and then enter the rapid development stage

Shuttle – basic function disassembly

  • Base – provides basic components, such as default pages and basic containers
  • Constants – provides constants and attributes for global use
  • Helper – global properties across components
  • Local – local storage
  • Network – network management, including encryption and decryption
  • Localization – multilingual management
  • Utlis – tool class
  • Widget – custom control
  • Constant config configuration file
  • Habit plug-in interaction management
  • view_ Model exposure
Explanation of flutter core functions
  • Basescaffold includes several core functions
    1. Global toolbar setting control
///Set toolbar
  final Widget toolBar;

The default toolbar widget is also provided

///Gets the parameters of the current toolbar
  Widget _findCurrentToolBar() {
    ///Toolbar with priority display settings
    if (widget.toolBar != null) {
      return widget.toolBar;
    }

    ///Displays the default toolbar
    if (widget.toolBar == null && widget.viewModel.appBarIsShow) {
      return AppBarWidget(widget.viewModel);
    }
    return null;
  }

2. Global default page control and customization

GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: () {
            KeyboardUtils.hideByContext(context);
          },
          child: ValueListenableBuilder<EmptyState>(
            valueListenable: widget.viewModel.emptyState,
            builder: (context, state, _) => state == EmptyState.NORMAL
                ? widget.body
                : BaseEmptyStateWidget<VM>(
                    toolBar: widget.toolBar,
                  ),
          ),
        )

3. Life cycle monitoring

///Page lifecycle
enum PageState {
  RESUMED,
  INACTIVE,
  PAUSED,
  DETACHED,
}
Declare the life cycle of the page and carry out relevant business development

Since this article is based on Android MVVM + databinding mode for fluent development, the data monitoring mode here isValueNotifier, the following is a brief description of the appbarwidget

import 'package:flutter/material.dart';
import 'package:habit/example/widget/base_view_model.dart';
import 'package:habit/habit.dart';

///Global toolbar
class AppBarWidget extends StatelessWidget with PreferredSizeWidget {
  final BaseViewModel appBarProperty;

  AppBarWidget(this.appBarProperty);

  @override
  Widget build(BuildContext context) {
    return ValueListenableListBuilder(
      valueListenables: [
        appBarProperty.appBarTitle,
        appBarProperty.appBarShowBackIcon,
        appBarProperty.appBarBackIconColor,
        appBarProperty.appBarTitleColor,
        appBarProperty.appBarTitleSize,
        appBarProperty.appBarBgColor,
        appBarProperty.appBarBrightness,
        // appBarProperty.appBarLeadingCallBack
      ],
      builder: (context, value, child) {
        return AppBar(
          brightness: appBarProperty.appBarBrightness.value,
          backgroundColor: appBarProperty.appBarBgColor.value==null
              ? Theme.of(context).accentColor
              : appBarProperty.appBarBgColor.value,
          elevation: 0,
          centerTitle: true,
          title: Text(
            appBarProperty.appBarTitle.value,
            style: TextStyle(
              fontSize: appBarProperty.appBarTitleSize.value,
              color: appBarProperty.appBarTitleColor.value,
              fontWeight: FontWeight.bold,
            ),
          ),
          leading: Visibility(
            visible: appBarProperty.appBarShowBackIcon.value,
            child: IconButton(
              onPressed: () {
                // appBarProperty.appBarLeadingCallBack.value?.call();
                ///Execute the default return button
                if (appBarProperty.appBarLeadingCallBack.value == null) {
                  Navigator.pop(context);
                } else {
                  appBarProperty.appBarLeadingCallBack.value.call();
                }
              },
              icon: Icon(
                Icons.arrow_back,
                color: appBarProperty.appBarBackIconColor.value,
                size: 25,
              ),
            ),
          ),
        );
      },
    );
  }

  @override
  Size get preferredSize => AppBar().preferredSize;
}

The full text focuses on valuelistenablelistbuilder, which is directly on the source code

class ValueListenableListBuilder<T> extends StatefulWidget {
  const ValueListenableListBuilder({
    Key key,
    @required this.valueListenables,
    @required this.builder,
    this.child,
  })  : assert(valueListenables != null),
        assert(builder != null),
        super(key: key);
  ///Look here, this is the key
  final List<ValueListenable<T>> valueListenables;

  final ValueListWidgetBuilder<T> builder;

  final Widget child;

  @override
  State<StatefulWidget> createState() => _ValueListenableListBuilderState<T>();
}

From the source code, we can know that valuelistenable is used to monitor data changes and refresh the UI, that is, the data-driven UI. Here, let’s consider for the moment. Is MVVM + livedata + databinding the same mode? In fact, it’s basically the same.

I’ll write an example casually. For comparison, bind data through mutablelivedata, and then bind VM in XML
xxviewModel.kt

/**
     *Video address observer
     */
    val videoUrl = MutableLiveData<String>(currentAlbum?.realPath)


 <com.example.widget.player.VideoPlayerView
            android:id="@+id/videoView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            binding:autoPlay="@{true}"
            binding:currentTimeTextView="@{current}"
            binding:layout_constraintBottom_toBottomOf="parent"
            binding:layout_constraintEnd_toEndOf="parent"
            binding:layout_constraintStart_toStartOf="parent"
            binding:layout_constraintTop_toTopOf="parent"
            binding:looping="@{true}"
            binding:playTag="@{viewModel.videoPlayTag}"
            binding:seekBar="@{progress}"
            binding:totalTimeTextView="@{total}"
            binding:videoUrl="@{viewModel.videoUrl}" />

When I finish writing, put the source code