# Быстрая разработка Flutter MVP Использование конкретного фреймворка не зафиксировано, их много в интернете, здесь мы строим только MVP фреймворк, фреймворк заимствует код, написанный другими большими парнями, а потом вносит какие-то коррективы под себя. ### Базовая конфигурация Во-первых, давайте посмотрим на библиотеки, которые зависят, и не обязательно все они используются, это зависит от потребностей вашего проекта. ``ямл зависимости: трепетать: СДК: флаттер # Следующее добавляет в ваше приложение шрифт Cupertino Icons. # Используйте с классом CupertinoIcons для иконок в стиле iOS. купертино_icons: ^0.1.2 # маршрутизация расход: ^1.5.1 # простое хранилище данных общие_настройки: ^0.5.3+4 # внедрение зависимости получить_ит: ^ 3.0.1 # тост флаттертост: ^ 3.1.3 # размер подходит flutter_screenutil: ^ 0.6.0 # Сканирование штрих-кода QR-кода флаттер_qr_reader: ^ 1.0.3 # Запрос на доступ разрешение_обработчик: ^3.0.0 # сетевой запрос Дио: ^ 2.0.1 # Мониторинг состояния сети мобильного телефона подключение: ^0.4.3+7 # шина сообщений event_bus: ^ 1.1.0 # Обновить загрузку pull_to_refresh: ^ 1.5.4 # Прием рксдарт: ^0.21.0 ``` **Инициализировать часть фреймворка в функции входа main():** ``` дротик пустая функция() { Router router = Router(); Routes.configureRoutes(маршрутизатор); Application.router = маршрутизатор; Application.setupLocator(); запустить приложение (Мое приложение ()); } ``` 1. Строки 1, 2 и 3 используются для настройки маршрутизации в проекте (фреймворк **fluro**). 2. **Application.setupLocator()** Инициализировать **get_it** структуру внедрения зависимостей, эквивалентную **dagger2** в Android. 3. Настройка экранизации также производится в Приложении. > Посмотрите на код в приложении: ``` дротик приложение класса { статический маршрутизатор Router; статический GlobalKey<NavigatorState> globalKey = GlobalKey(); статические SharedPreferences sp; статическая ширина двойного экрана; статический двойной экранВысота; статический двойной статусBarHeight; статический GetIt getIt = GetIt.instance; статический initSp() асинхронный { sp = ожидание SharedPreferences.getInstance(); } статический initScreenUtil (контекст BuildContext) { ScreenUtil.instance = ScreenUtil(ширина: 750, высота: 1334) ..init(контекст); окончательный размер = MediaQuery.of(context).size; Приложение.ширина экрана = размер.ширина; Application.screenHeight = размер.высота; Application.statusBarHeight = MediaQuery.of(context).padding.top; } статический setupLocator(){ getIt.registerSingleton(NavigateService()); getIt.registerSingleton(DioRequest()); getIt.registerSingleton(ApiService()); getIt.registerSingleton(SettingPresenter()); getIt.registerSingleton(LoginPresenter()); getIt.registerSingleton(CollectionPresenter()); getIt.registerSingleton(BillPresenter()); } } ``` > Посмотрите на все коды в main.dart, базовая структура инициализирована и страница входа открывается напрямую ``` дротик пустая функция() { Router router = Router(); Routes.configureRoutes(маршрутизатор); Application.router = маршрутизатор; Application.setupLocator(); запустить приложение (Мое приложение ()); } класс MyApp расширяет StatelessWidget { // Этот виджет является корнем вашего приложения. @переопределить Сборка виджета (контекст BuildContext) { вернуть MaterialApp( название: 'Демонстрация флаттера', navigatorKey: Application.getIt<NavigateService>().globalKey, theme: ThemeData(///настройки темы проекта яркость: Яркость.свет, основной цвет: цвет (0xFF00A0E9), всплескЦвет: Colors.transparent, backgroundColor: Цвет (0xFFF2F2F2), ), домашняя страница: LoginPage(), onGenerateRoute: Application.router.generator, /// генерация маршрута debugShowCheckedModeBanner: правда, ); } } ``` ### Начните создавать MVP > base_contract.dart ``` дротик абстрактный класс IBaseView { } абстрактный класс IBasePresenter<T расширяет IBaseView> { /// привязать вид недействительным прикрепитьView (представление T); /// Отдельный вид недействительным detachView(); } абстрактный класс IBaseModel { } ``` > base_view.dart (наследуется от IBaseView в base_contract.dart) ``` дротик импортировать 'base_contract.dart'; класс BaseView расширяет IBaseView { недействительным showLoading ({String msg}) { } недействительным closeLoading () { } недействительными renderPage (объект объекта) { } недействительная перезагрузка () { } недействительным showError({String errorMsg}) { } недействительным showDisConnect () { } } ``` **Далее создайте три базовых абстрактных класса** > base_page.dart (реализует IBaseView в base_contract.dart) ``` дротик абстрактный класс BasePageState<T расширяет BasePresenter> расширяет State<StatefulWidget> с помощью AutomaticKeepAliveClientMixin, реализует BaseView { Ведущий T = Application.getIt.get<T>(); логическое значение _isPrepared = ложь; @mustCallSuper///Обратите внимание на эту аннотацию, важно вызывать этот метод родительского класса в подклассе, иначе ///презентатор подкласса сообщит о нулевом указателе вида @переопределить Сборка виджета (контекст BuildContext) { _attachView(); если (!_isПодготовлено) { Timer.run(() => preparePage()); _isПодготовлено = Истина; } вернуть ноль; } @mustCallSuper @переопределить недействительным распоряжаться () { супер.распоряжаться(); _detachView(); } /// Инициализировать один раз = "Используется докладчиком для запроса сетевых данных, а затем вызова showDialog для получения подходящего контекста и сообщения об ошибке Пустая подготовка страницы (); @переопределить недействительная перезагрузка () {} @переопределить недействительными renderPage (объект o) {} @переопределить недействительным showDisConnect () {} @переопределить недействительным showError({String errorMsg}) { // TODO: реализовать showError если (errorMsg != ноль) { ToastUtil.showToast(errorMsg); } } @переопределить недействительным showLoading ({String msg}) { // TODO: реализовать showLoad /// Отделить показ диалога от обычной страницы показатьдиалог( контекст: контекст, барьерDismissible: false, строитель: (контекст BuildContext) { вернуть диалог загрузки ( текст: msg ?? 'Загрузка', ); }); } @переопределить недействительным closeLoading () { // TODO: реализовать closeLoading /// Должен быть связан с методом showLoading, чтобы избежать появления текущей страницы Navigator.pop(контекст); } недействительным _attachView () { if(null!= ведущий){ ведущий.attachView(этот); } } недействительным _detachView () { if(null!= ведущий){ ведущий.detachView(); } } @переопределить // TODO: реализуем запрос KeepAlive bool get wantKeepAlive => true; } ``` > base_presenter.dart (реализует IBasePresenter в base_contract.dart) ``` дротик импортировать 'base_contract.dart'; абстрактный класс BasePresenter<T расширяет IBaseView> реализует IBasePresenter<T> { вид Т; @переопределить недействительным прикрепитьView (представление T) { это.представление = представление; } @переопределить недействительным detachView () { если (нуль!= вид) { это.представление = ноль; } } } ``` > base_model.dart (реализует IBaseModel в base_contract.dart) ``` дротик класс BaseModel реализует IBaseModel { APIService apiService = Application.getIt.get<ApiService>(); } ``` ### Приложение (логин) > login_contract.dart ``` дротик импортировать 'пакет:flutter_mvp/base/base_contract.dart'; импортировать «пакет: rxdart/rxdart.dart»; абстрактный класс ILoginView расширяет IBaseView { /// Перейти на главную страницу недействительными navigationHome(); /// Показать запрос на загрузку недействительным showLoading ({String msg}); /// Закрыть окно загрузки недействительным closeLoading(); /// Получить учетную запись пользователя Строка getAccount(); /// Получить пароль пользователя Строка getPassword(); } абстрактный класс ILoginPresenter расширяет IBasePresenter<ILoginView> { /// Операция входа недействительный логин(); } абстрактный класс ILoginModel расширяет IBaseModel{ /// Запрос входа Наблюдаемый логин (учетная запись String, пароль String); } ``` > login_page.dart ``` дротик import 'package:flutter/cupertino.dart'; импортировать 'пакет: флаттер/material.dart'; импортировать 'пакет:flutter_mvp/app/application.dart'; импортировать 'пакет:flutter_mvp/base/base_page.dart'; импортировать 'пакет:flutter_mvp/route/navigator_util.dart'; импортировать 'пакет:flutter_screenutil/flutter_screenutil.dart'; импортировать 'login_contract.dart'; импортировать «login_presenter.dart»; класс LoginPage расширяет StatefulWidget { @переопределить LoginPageState createState() => LoginPageState(); } класс LoginPageState расширяет BasePageState<LoginPresenter>, реализует ILoginView{ _navigateHomePage (контекст BuildContext) { NavigatorUtil.goHomePage (контекст, заменить: true); } TextEditingController _accountController = TextEditingController(); TextEditingController _passwordController = TextEditingController(); Цвет _btnColor = Цвет (0xff00A0E9); логический _isObscureText = истина; недействительным _setStatus (статус int) { setState (() { переключатель (статус) { случай 0: _авторизоваться(); перерыв; Дело 1: _btnColor = Цвет (0xff0083BF); перерыв; случай 2: _btnColor = Цвет (0xff00A0E9); перерыв; } }); } недействительным _closeOrOpenEye () { setState (() { _isObscureText = !_isObscureText; }); } @переопределить недействительным initState () { _accountController.text = '00113582010041'; _passwordController.text = '123456'; супер.initState(); } @переопределить Сборка виджета (контекст BuildContext) { Application.initScreenUtil(контекст); супер.сборка(контекст); _accountController.addListener((){}); _passwordController.addListener (() {}); вернуть эшафот( тело: Контейнер( цвет: Цвета.белый, дочерний элемент: ListView( дети: <Виджет>[ /// Номер телефона Контейнер( поле: EdgeInsets.only( слева: ScreenUtil().setWidth(50), справа: ScreenUtil().setWidth(50), сверху: ScreenUtil().setHeight(580)), ребенок: Текстовое поле( контроллер: _accountController, украшение: InputDecoration( hintText: 'Пожалуйста, введите свой номер телефона', ), стиль: TextStyle(fontSize: ScreenUtil().setSp(30)), ), ), /// пароль Контейнер( поле: EdgeInsets.only( слева: ScreenUtil().setWidth(50), справа: ScreenUtil().setWidth(50), сверху: ScreenUtil().setHeight(50)), ребенок: Стек( дети: <Виджет>[ Текстовое поле( обскуретекст: _isObscureText, textInputAction: TextInputAction.done, контроллер: _passwordController, украшение: InputDecoration( hintText: 'Введите пароль', ), стиль: TextStyle(fontSize: ScreenUtil().setSp(30)), ), /// Маленькие глаза Позиционировано( ребенок: Центр( ребенок: GestureDetector( дочерний элемент: Image.asset('assets/images/${_isObscureText ? 'icon_eye_close' : 'icon_eye_open'}.png'), onTap: _closeOrOpenEye, ), ), справа: 0, сверху: ScreenUtil().setHeight(25), ) ], ), ), /// забыли пароль Ряд(), /// Авторизоваться Детектор жестов( onTapDown: (_) => _setStatus(1), onTapUp: (_) => _setStatus(2), onTap: () => _setStatus(0), ребенок: Контейнер( высота: ScreenUtil().setHeight(80), поле: EdgeInsets.only( слева: ScreenUtil().setWidth(75), справа: ScreenUtil().setWidth(75), сверху: ScreenUtil().setHeight(227)), ребенок: Центр( ребенок: Текст( 'Авторизоваться', стиль: TextStyle( цвет: Цвета.белый, размер шрифта: ScreenUtil().setSp(34), ), ), ), украшение: BoxDecoration( borderRadius: BorderRadius.all( Радиус.круглый( ScreenUtil().setWidth(10), ), ), цвет: _btnColor, ), ), ), ], ), ), ); } недействительным _логин () асинхронный { ведущий.логин(); } @переопределить недействительными preparePage () { // TODO: реализовать preparePage } @переопределить Строка getAccount() { // TODO: реализовать getAccount вернуть _accountController.text; } @переопределить Строка getPassword () { // TODO: реализовать getPassword вернуть _passwordController.text; } @переопределить недействительными navigationHome () { // TODO: реализовать navigationHome NavigatorUtil.goHomePage (контекст, замена: правда); } } ``` > login_presenter.dart ``` дротик импортировать 'пакет:flutter_mvp/base/base_presenter.dart'; импортировать 'пакет:flutter_mvp/exception/exception.dart'; import 'package:flutter_mvp/pojo/response/login_rsp.dart'; импортировать 'пакет:flutter_mvp/utils/sputil.dart'; импортировать 'login_contract.dart'; импортировать 'login_model.dart'; класс LoginPresenter расширяет BasePresenter<ILoginView> реализует ILoginPresenter { ILoginModel _loginModel; Логин Презентер() { _loginModel = Модель входа(); } @переопределить недействительный логин () { // TODO: реализовать логин view.showLoading(msg: 'Вход в систему...'); _loginModel.login(view.getAccount(), view.getPassword()).listen((dataMap) { вид.closeLoading(); print('Данные ответа на вход: ${dataMap.toString()}'); LoginRsp loginRsp = LoginRsp.fromJson(dataMap); SpUtil.setToken(loginRsp.access_token); }, onError: (ошибка) { вид.closeLoading(); если (ошибка CommonException) { print('Ошибка входа: ${error.errorMsg}'); } еще { print('Ошибка входа: ${error.toString()}'); } }, onDone: () { //В реальной разработке только логин успешен перед прыжком вид.navigateHome(); }); } } ``` > login_model.dart ``` дротик импортировать 'пакет:flutter_mvp/base/base_model.dart'; import 'package:rxdart/src/observables/observable.dart'; импортировать 'login_contract.dart'; класс LoginModel расширяет BaseModel, реализует ILoginModel { @переопределить Наблюдаемый логин (учетная запись String, пароль String) { // TODO: реализовать логин вернуть apiService.login(учетная запись, пароль); } } ``` ### Другая конфигурация 1. Адаптация образа: создайте пути assets/images/2.0x и assets/images/3.0x в корневом каталоге проекта и поместите разные изображения с одинаковыми именами в /images/, /images/2.0x/, /images. /3.0x/ путь, затем в pubspec.yaml flutter:assets:add - assets/images/ 2. Стартовая страница Android: поместите изображение стартовой страницы в соответствующую папку с возможностью рисования в пути Android res и в res/drawable/launch_background.xml измените src тега растрового изображения на имя изображения стартовой страницы. ### Прикрепите адрес [flutter_mvp](https://github.com/zhaoZhao0213/flutter_mvp.git)
Быстрая разработка Flutter MVP
Guess you like
Origin blog.csdn.net/ren1027538427/article/details/122781780
Recommended
Ranking