preface

BaseFlutter open source project based on Flutter2.0, syntax overhead security, is the best practice of Provider MVVM, can be used for online applications of excellent architecture, the project uses a lot of actual project development needs to use technology and third-party frameworks, and do a lot of basic packaging. This project can be directly used in practical projects. Learning this project can also help beginners to reduce learning difficulty, provide learning directions, and achieve a quick start to Flutter

The code structure

Some code examples

abstract class BaseState<W extends StatefulWidget, VM extends BaseViewModel> extends State<W> with BaseStateInterface, NavigatorMixin, ToastMixin, EventBusMixin, SharePreferenceMixin, ScreenAdapterMixin{ late VM viewModel; // We need to change this to late. Otherwise, the Provider will report an EventBus error. eventBus; LoadingDialog? loadingDialog; late bool isBuildFinish; @override void initState() { super.initState(); isBuildFinish = false; WidgetsBinding widgetsBinding = WidgetsBinding.instance! ; WidgetsBinding. AddPostFrameCallback ((the callback) {/ / that build over print (" = = = = > build over "); isBuildFinish = true; onBuildFinish(); }); setContext(context); setIsDispose(false); viewModel = getIt.get<VM>(); viewModel.context = context; viewModel.init(); viewModel.showLoadingFun = () { showLoading(); }; viewModel.dismissLoadingFun = () { loadingDialog? .dismissDialog(); }; initEventBus(); } @override void onBuildFinish() {} @override void initEventBus() { if (eventBus == null) { eventBus = EventBus.get(); }} void showLoading() async {if (isBuildFinish) {// Must wait until the parent component builds, https://blog.csdn.net/qq_39493848/article/details/108514136 showDialog( context: context, builder: (_) { if (loadingDialog == null) { loadingDialog = LoadingDialog(); } return loadingDialog! ; }); } else { await Future.delayed(Duration(milliseconds: 10)); showLoading(); } } @override void dispose() { super.dispose(); setIsDispose(true); viewModel.showLoadingFun = null; viewModel.dismissLoadingFun = null; }}Copy the code
abstract class BaseViewModel<M extends Object> extends ChangeNotifier with BaseViewModelInterface, NavigatorMixin, ToastMixin, SharePreferenceMixin, EventBusMixin, DataBaseMixin { int _loadNum = 0; int _minLoadNum = 1; late BuildContext context; late M model; bool _isDispose = false; bool get isDispose => _isDispose; int needLoadingRequestCount = 0; bool isLoading = false; Function()? showLoadingFun; Function? dismissLoadingFun; static bool isNeedCatchError = false; set minLoadNum(int value) { _minLoadNum = value; } set loadNum(int value) { _loadNum = value; } int get loadNum { return _loadNum; } void notifyPage() { if (! _isDispose) { loadNum++; print("====>loadNum:$loadNum"); if (_loadNum >= _minLoadNum) { print("====>notifyListeners"); notifyListeners(); } } } @override void init() { model = getIt.get<M>(); setContext(context); setIsDispose(false); } void showLoading(bool isNeedLoading) { if (isNeedLoading) { needLoadingRequestCount++; if (! isLoading) { isLoading = true; if (showLoadingFun ! = null) { showLoadingFun! .call(); } showLoadingFun? .call(); } } } void dismissLoading(bool isNeedLoading) { if (isNeedLoading) { needLoadingRequestCount--; if (needLoadingRequestCount == 0) { isLoading = false; if (dismissLoadingFun ! = null) { dismissLoadingFun! .call(); } dismissLoadingFun? .call(); }}} // Start a network request while handling the exception, loading void sendRequest<T>(Future<T> future, FutureOr<dynamic> onValue(T value), {Function(Exception e)? error, bool isNeedLoading = false}) { showLoading(isNeedLoading); future.then((t) { dismissLoading(isNeedLoading); onValue(t); }); if (isNeedCatchError) { future.catchError((e) { dismissLoading(isNeedLoading); print("====>error:$e"); if (error ! = null) { error(e); }}); } } @override void dispose() { super.dispose(); _isDispose = true; setIsDispose(_isDispose); }}Copy the code
@injectable class LoginViewModel extends BaseViewModel<LoginModel> { @factoryMethod LoginViewModel(); String loginName = ""; String psw = ""; Void login() {if (loginname.isempty) {showToast(" Login account cannot be empty "); } else if (psw.isempty) {showToast(" Login password cannot be empty "); } else { sendRequest<LoginResult>(model.login(loginName, psw), (value) { if (value.errorCode == 0) { value.data? .let((it) { UserInfoSp.getInstance().uid = it.id ?? 0; UserInfoSp.getInstance().token = it.token ?? ""; UserInfoSp.getInstance().userName = it.username ?? ""; }); pop(); push(MainPage()); } else { showToast(value.errorMsg!) ; } }, isNeedLoading: true); }}}Copy the code

Third-party frameworks used

  • Injectable works with the Get_IT framework to generate code at compile time for dependency injection
  • 2. Dio realizes network request
  • 3. Get_it implements dependency injection
  • 4. Retrofit implements network requests with DIO, generating code for network requests at compile time
  • 5. Print logger logs
  • 6. Toast toast
  • 7. Event_bus implements communication between different pages and components
  • 8. Json_serializable implements JSON data serialization with JSON_annotation
  • 9. Extended_image implements the loading of network images, powerful official Image extension component, supporting loading and failure display, caching network images, zooming and dragging pictures, drawing custom effects and other functions
  • 10. Webview_flutter loads web pages
  • 11. Shared_preferences Simple persistent data storage
  • 12. Pull_to_refresh implements pull-down refresh and paging loading
  • 13. Floor database, using similar to RetroFIT
  • 14. Flutter_swiper Image rotation

Architecture and base encapsulation used

  • Based on Flutter2.0, syntax is null safe
  • Combined with Provider to achieve MVVM architecture, encapsulation of BaseState, BaseStatefulWidget, BaseViewModel
  • Combined with template method pattern, generics, Mixin, dependency injection and other methods, encapsulates a lot of repeated logic, simplifying the difficulty of development
  • Mixin class encapsulation: currently contains NavigatorMixin, ToastMixin, SharePreferenceMixin, EventBusMixin, and DataBaseMixin
  • Encapsulation of basic widgets: such as BottomDialog, CenterDialog, EnsureAndCancelDialog, LoadingDialog, PopupWindow, CommonWrap, LazyIndexedStack, etc
  • BaseViewModel Unifies network requests, initiates network requests, and handles exceptions and loading at the same time
  • Use extension functions: Extend from Object, List, int, and Widget to make your code more concise and elegant

In the late planning

1. Routing to realize the decoupling of each module and each service 2. Componentization 3. Buried frame 5. Various cool animations 6. Performance optimization

QQ communication group of

Group number: 770892444