Быстрая разработка Flutter MVP

# Быстрая разработка 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)

Guess you like

Origin blog.csdn.net/ren1027538427/article/details/122781780
MVP
Recommended