лучшие практики флаттера + getx

внешний интерфейс Flutter
лучшие практики флаттера + getx

Адрес склада:GitHub.com/Се Йези/Приложение…

скажи это прямо

Привет, давно не виделись, друзья, в этот раз привожу практическую статью по flutter + getx.

на основеgetxРеализовано совершенно новоеflutter getxШаблон, подходящий для разработки средних и крупных проектов.

  • 💥flutterпоследняя версия нулевого сейфа
  • 🍀viewа также逻辑полностью развязанный
  • viewа такжеstateавтоматический ответ
  • 📦dio,shared_preferencesПакеты такого распространенного модуля
  • Выйдиcontextизменять

окружающая обстановка

Flutter 2.2.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision b22742018b (3 weeks ago) • 2021-05-14 19:12:57 -0700
Engine • revision a9d88a4d18
Tools • Dart 2.13.0

подразделение каталога lib

  • common

Этот каталог используется для хранения общих модулей и их переменных, таких какcolors,langs,valuesи т.д., например:

├── colors
│   └── colors.dart
├── langs
│   ├── en_US.dart
│   ├── translation_service.dart
│   └── zh_Hans.dart
└── values
    ├── cache.dart
    ├── storage.dart
    └── values.dart
  
  • components

В этом каталоге в основном хранятся компоненты объявлений верхнего уровня, такие какappbar,scaffold,dialogд., например:

├── components.dart
├── custom_appbar.dart
└── custom_scaffold.dart
  • pages

В этом каталоге в основном хранятся файлы подкачки, например:

Примечание. Каждый элемент представляет собой папку.

├── Index
├── home
├── login
├── notfound
├── proxy
└── splash

  • router

Этот каталог является файлом маршрутизации, и соглашение о маршрутизации этого шаблона命名路由, является фиксированным каталогом, структура каталогов выглядит следующим образом:

├── app_pages.dart
└── app_routes.dart

  • services

Этот каталог используется для храненияAPI,Например:

├── services.dart
└── user.dart  // 关于用户的API
  • utils

Этот каталог используется для хранения некоторых модулей инструментов, таких какrequest,local_storageд., например:

├── authentication.dart
├── local_storage.dart
├── request.dart
├── screen_device.dart
└── utils.dart

спецификация разработки

Когда вам нужно создать новую страницу, вам необходимо выполнить следующие шаги:

Предположим, теперь мы хотим создатьHomeстраница.

  1. существуетpagesновый каталогhomeсодержание:
// pages

$ mkdir home
$ cd home
  1. существуетhomeВ каталоге создайте следующие четыре файла:
  • home_view.dart: Вид (используется для реализации макета страницы)

  • home_contrller.dart: Контроллер (используется для реализации бизнес-логики)

  • home_binding: привязка контроллера (используется для привязкиcontrollerприбытьview)

  • home_model: модель данных (используется для согласования модели данных)

Примечание. Это необходимо делать каждый раз при создании страницы с именем '页面名_key' такая форма.

При создании страницы оглавление должно выглядеть так👇:

// home
.
├── home.binding.dart
├── home_controller.dart
├── home_model.dart
└── home_view.dart
  1. прибытьrouterДобавьте соответствующий маршрут в папку:
// app_routes.dart
part of 'app_pages.dart';
abstract class AppRoutes {
  ...
  static const Home = '/home';
  ...
}
// app_pages.dart
class AppPages {

  static final routes = [
    ...
    GetPage(
      name: AppRoutes.Home,
      page: () => HomePage(),
      binding: HomeBinding(),
    ),
    ...
  ];
}

Выполнив вышеуказанные шаги, вы можете начать счастливо развиваться.

государственное управление

contrllerЗдесь мы реализуем бизнес-логику, почему мы отделяем бизнес-логику от представлений? потому чтоflutterКод в стиле спагетти слишком сложно поддерживать, иflutterМакет и стиль страницы отвратительно писать вместе, плюс код бизнес-логики, его слишком сложно поддерживать, и если мы хотим иметь состояние, наша страница должна наследоваться отstateful widget, потеря производительности слишком серьезна.

Поэтому мы используемgetxкоторый предоставилcontroller, чтобы отделить нашу бизнес-логику от наших представлений.

стандартcontrllerЭто выглядит так:

class HomeController extends GetxController {
  final count = 0.obs;

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {}

  @override
  void onClose() {}

  void increment() => count.value++;
}

Когда нам нужна реактивная переменная, мы просто добавляем переменную после переменной.obs,Например:

final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;

// 甚至自定义类 - 可以是任何类
final user = User().obs;

Стоит отметить, что поскольку сейчасflutterимеютnull-safety, поэтому нам лучше дать реактивной переменной начальное значение.

когда мыcontrollerПредставления автоматически обновляют визуализацию при обновлении реактивных переменных.

Но на самом деле можно и не определять такого рода реактивную переменную, например, мы можем сделать так:

class HomeController extends GetxController {
  int count = 0;

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {}

  @override
  void onClose() {}

  void increment() {
    count++;
    update();
  } 
}

такой и.obsЕдинственная разница в том, что нам нужно вручную вызватьupdate()Обновить изменения состояния, подобные этомуviewбыть способнымcountКогда он изменится, получите уведомление для повторного рендеринга.

Мы должны поместить инициирующий запрос вonInitВ хуке, например, при входе на страницу заказа мы должны получить информацию о заказе, как и вstateful wdigetвнутриinitКак крючки.

Посмотреть

Во-первых, вам нужно поставить свойclassунаследовано отGetxView<T>(T — ваш контроллер), например:

class HomePage extends GetView<HomeController> {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
    );
  }
}

GetxView<HomeController>автоматически поможет вамControllerвводить вview, вы можете просто понять, что он автоматически выполняет следующие шаги для вас

final controller = Get.find<HomeController>();

не нужно беспокоитьсяGetxView<T>производительность, так как он просто наследуется отStateless Widget, помните, что сgetxтебе не нужноStateful Widget

когда мы хотим связатьcontrollerпеременная, мы договорились о двух методах:

  1. Obx(()=>)

если ваша переменная.obs, то используемObx(()=>), который автоматически обновляется при изменении переменнойview,Например:

// home_contrller
class HomeController extends GetxController {
  final count = 0.obs;

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {}

  @override
  void onClose() {}

  void increment() => count.value++;
}

существуетviewвнутри использованиеObx(()=>)связыватьcount:

// home_view
class HomePage extends GetView<HomeController> {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Obx(() => Center(child: Text(controller.count.toString()))),
      ),
    );
  }
}
  1. GetBuilder<T>

если ваша переменная не.obs, то используемGetBuilder<T>,Например:

class HomeController extends GetxController {
  int count = 0;

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {}

  @override
  void onClose() {}

  void increment() {
    count++;
    update();
  } 
}

существуетviewвнутри использованиеGetBuilder<T>связыватьcount:

class HomePage extends GetView<HomeController> {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(
      appBar: MyAppBar(
        centerTitle: true,
        title: MyTitle('首页'),
        leadingType: AppBarBackType.None,
      ),
      body: Container(
        child: GetBuilder<HomeController>(builder: (_) {
          return Center(child: Text(controller.count.toString()));
        }),
      ),
    );
  }
}

фактическиgetxтакже предоставляет другиеrender function, но чтобы уменьшить умственную нагрузку и сложность, мы просто используем эти два.

управление маршрутом

Здесь мы используемgetxПредоставляет именованную маршрутизацию, если вы узналиvue, то почти нет затрат на обучение.

Предположим, теперь мы добавляем страницу с именемList, то нам нужноrouterПапка для его настройки:

// app_routes.dart
part of 'app_pages.dart';
abstract class AppRoutes {
  ...
  static const List = '/list';
  ...
}
// app_pages.dart
class AppPages {

  static final routes = [
    ...
    GetPage(
      name: AppRoutes.Home,
      page: () => ListPage(),
      binding: ListBinding(),
    ),
    ...
  ];
}

этоListСоответствующим предположением является список заказов.Когда мы нажимаем на заказ в списке, мы обычно попадаем на страницу сведений о заказе, поэтому в это время мы должны добавить еще одну страницу сведений:

// app_routes.dart
part of 'app_pages.dart';
abstract class AppRoutes {
  ...
  static const List = '/list';
  static const Detaul = '/detail';
  ...
}
// app_pages.dart
class AppPages {

  static final routes = [
    ...
    GetPage(
      name: AppRoutes.Home,
      page: () => ListPage(),
      binding: ListBinding(),
      children: [
        GetPage(
          name: AppRoutes.Detail,
          page: () => DetailPage(),
          binding: DetailBinding(),
        ),
      ],
    ),
    ...
  ];
}

Поскольку страница сведений и страница списка имеют отношение приоритета, мы можем поместитьDetailстраница, поставитьListизchildrenНиже, конечно, можно поступить иначе.

Когда мы используем:

Get.toNamed('/list/detail');

Другие хуки маршрутизации:

Просмотрите и удалите предыдущую страницу:

Get.offNamed("/NextScreen");

Просмотрите и удалите все предыдущие страницы:

Get.offAllNamed("/NextScreen");

Параметры передачи:

Get.toNamed("/NextScreen", arguments: {id: 'xxx'});

Тип параметра может быть строкой, картой, списком или даже экземпляром класса.

Параметры приобретения:

print(Get.arguments);
// print out: `{id: 'xxx'}`

использоватьgetxмаршрутизация имеет очень приятное преимущество, что она去context化из. Помните, когда мы былиcontextДоминировать над страхом? имеютgetx, он перестанет существовать.

использоватьmonia-cliразвивать

Мы очень счастливы, могутflutter-getx-templateПрисоединяйсяmonia-cli.

использоватьmonia-cliновыйflutterпроект:

monia create <project-name>
➜  Desktop monia create flutter_demo
? Which framework do you want to create Flutter
? Which flutter version do you want to create null-safety
? Please input your project description description
? Please input project version 1.0.0

✨  Creating project in /Users/xieyezi/Desktop/flutter_demo.

🗃  Initializing git repository....
.......
⠏ Download template from monia git repository... This might take a while....

🎉  Successfully created project flutter_demo.
👉  Get started with the following commands:

$ cd flutter_demo
$ flutter run

                        _                  _ _ 
  _ __ ___   ___  _ __ (_) __ _        ___| (_)
 | '_ ` _ \ / _ \| '_ \| |/ _` |_____ / __| | |
 | | | | | | (_) | | | | | (_| |_____| (__| | |
 |_| |_| |_|\___/|_| |_|_|\__,_|      \___|_|_|

Не только это,monia-cliТакже обеспечивает быстрое созданиеflutter getxфункция страницы.

Предположим, теперь вы хотите сгенерироватьorder_sendingновая страница, вам просто нужноpagesВойдите в следующий каталог:

monia init order_sending
➜  pages monia init order_sending
✨  Generate page in /Users/xieyezi/Desktop/flutter_demo/lib/pages/order_sending.
⠋ Generating, it's will not be wait long...
generate order_sending lib success.
generate /Users/xieyezi/Desktop/flutter_demo/lib/pages/order_sending/order_sending_view.dart file success.
generate /Users/xieyezi/Desktop/flutter_demo/lib/pages/order_sending/order_sending_controller.dart file success.
generate /Users/xieyezi/Desktop/flutter_demo/lib/pages/order_sending/order_sending_binding.dart file success.

🎉  Successfully generate page order_sending.

Плагин vscode

moniaтакже обеспечиваетvscodeПлагин:monia-vscode-extension

Нажмите в левом нижнем углуmonia-generateтекстовая кнопка, введитеpageName, ты сможешьpagesСоздайте новый в каталогеflutter getx page:

example.gif

Официальная ссылка

государственное управление управление маршрутом управление зависимостями