предисловие
get | Flutter Package (flutter-io.cn)всегдаFlutter
В нем есть противоречивая трехсторонняя библиотека. Именно из-за полемики, поэтому мы должны иметь собственное суждение, не стоя в очереди.
с одной стороны
Это самая популярная библиотека в pub.dev.
Более 5500 звезд Github
Имеет более 140 участников
1400+ выпусков
Все это показывает, что это очень популярная сторонняя библиотека компонентов.
с другой стороны
Это также является объектом жалоб разработчиков.
Видно что слоты еще заполнены.Нажимать на стол пока не будем.Давайте сначала разберемся что естьGetX
.
Основная тема
GetX — легкое и мощное решение на Flutter: высокопроизводительное управление состоянием, интеллектуальное внедрение зависимостей и удобное управление маршрутизацией — из официального описания.
То же самое относится и к трем основным функциям, представленным в официальной документации.
давайте загрузимGetX
проект, откройте его, чтобы увидеть, как выглядит структура.
- Из папки выше можно примерно увидеть функции, за которые отвечает каждая часть.
get_connect
: Связанный с сетью
get_instance
: связанные с инъекцией
get_navigation
: связанные с маршрутизацией
get_rx
: Магия (собачья голова)
get_state_manager
: связанный со статусом
- Надо сказать, что здорово поддерживать документы из разных стран. Конечно, это для тех, кто будет читать документацию.
Далее я проанализирую его с точки зрения исходного кода.GetX
три функции.
управление зависимостями
Пучок依赖管理
Упоминается в начале, потому что другие 2 функции более или менее основаны на нем.
определить класс
В большинстве случаев этот класс необходимо наследоватьGetxController
, чтобы вся система делала это автоматическиdispose
операции (эта часть будет обсуждаться в управлении маршрутизацией).
class FFController extends GetxController {}
регистр
// 普通方式
Get.put<FFController>(FFController());
// 如果你想这个实例永远存在,不被删除,可以把 permanent 设置为 true
Get.put<FFController>(FFController(), permanent: true);
// 如果你的场景中,会存在多个相同的 FFController 实例,你可以用 tag 来进行区分
Get.put<FFController>(FFController(), tag: 'unique key');
// 使用的时候才创建类
Get.lazyPut<FFController>(() => FFController());
// 注册一个异步实例
Get.putAsync<FFController>(() async => FFController());
Получать
// 普通方式
FFController controller = Get.find<FFController>();
// 如果你的场景中,会存在多个相同的 FFController 实例,你可以用 tag 来进行区分
FFController controller = Get.find<FFController>(tag: 'unique key');
принцип
По сути, вы вводите кодGet.put
илиGet.find
, что в конечном итоге указывает наGetInstance
.
GetInstance
На самом деле единичный случай(Dart
Один поток действительно ароматный? ), который использует_singl
Карта хранит ваши зарегистрированные объекты/фабричные методы, конкретный процесс не показан.
class GetInstance {
factory GetInstance() => _getInstance ??= GetInstance._();
const GetInstance._();
static GetInstance? _getInstance;
T call<T>() => find<T>();
/// Holds references to every registered Instance when using
/// `Get.put()`
static final Map<String, _InstanceBuilderFactory> _singl = {};
/// Holds a reference to every registered callback when using
/// `Get.lazyPut()`
// static final Map<String, _Lazy> _factory = {};
}
государственное управление
Прежде чем говорить об этой части, позвольте мне повторить, что независимо от того, как работает фреймворк, в конечном итоге он вернется кsetState(() {});
.
Obx
ЭтоGetX
Одна из самых больших магий, давайте посмотрим, как она работает.
obs
Мы первыеFFController
добавить один<int>[]
переменная массива,obs
это метод расширения, который вернетRxList<int>
, а что такоеRxList
, мы пока не будем вдаваться в подробности, давайте сначала посмотрим, как он используется.
class FFController extends GetxController {
RxList<int> list = <int>[].obs;
}
Obx
использоватьObx
Включите раздел, который должен обновить статус, нажмитеIcons.add
кнопку, и у вас будет изменен весь список.
class RxListDemo extends StatelessWidget {
const RxListDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
FFController controller = Get.put<FFController>(FFController());
return Scaffold(
appBar: AppBar(),
body: Obx(
() {
return ListView.builder(
itemBuilder: (BuildContext b, int index) {
return Text('$index:${controller.list[index]}');
},
itemCount: controller.list.length,
);
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
controller.list.add(Random().nextInt(100));
},
),
);
}
}
принцип
Во-первых, посмотрите наRxList
Что это такое. Здесь размещена только часть кода, вы можете увидетьRxList
дляList
Итак, методы и операции были сделаныoverride
, и позвонитьrefresh
метод.
@override
void operator []=(int index, E val) {
_value[index] = val;
refresh();
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
@override
RxList<E> operator +(Iterable<E> val) {
addAll(val);
refresh();
return this;
}
@override
E operator [](int index) {
return value[index];
}
@override
void add(E item) {
_value.add(item);
refresh();
}
а такжеrefresh
выполнитьStream.add
метод. ТакStream
Кто его потребляет?
GetStream<T> subject = GetStream<T>();
final _subscriptions = <GetStream, List<StreamSubscription>>{};
void refresh() {
subject.add(value);
}
Давайте посмотримObx
Что спрятано внутри.
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
а такжеObx
унаследовано отObxWidget
.ObxWidget
ЯвляетсяStatefulWidget
,существует_ObxState
при инициализации_observer
Сделал прослушиватель, он сработает, когда будет уведомлен_updateTree
, что является нашим общимsetState(() {});
.
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key? key}) : super(key: key);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties..add(ObjectFlagProperty<Function>.has('builder', build));
}
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
class _ObxState extends State<ObxWidget> {
final _observer = RxNotifier();
late StreamSubscription subs;
@override
void initState() {
super.initState();
subs = _observer.listen(_updateTree, cancelOnError: false);
}
void _updateTree(_) {
if (mounted) {
setState(() {});
}
}
@override
void dispose() {
subs.cancel();
_observer.close();
super.dispose();
}
@override
Widget build(BuildContext context) =>
RxInterface.notifyChildren(_observer, widget.build);
}
пока вRxInterface.notifyChildren
лейтенант метода_observer
пройти в. На самом деле, мы можем видеть, что этот метод делает только одну вещь, вbuilder
Перед выполнением обратного вызова установитеRxInterface.proxy
для текущего_ObxState
середина_observer
.
/// Avoids an unsafe usage of the `proxy`
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final _observer = RxInterface.proxy;
RxInterface.proxy = observer;
final result = builder();
if (!observer.canUpdate) {
RxInterface.proxy = _observer;
throw """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = _observer;
return result;
}
пока вbuilder
в методеcontroller.list[index]
а такжеcontroller.list.length
когда звонили.
return ListView.builder(
itemBuilder: (BuildContext b, int index) {
return Text('$index:${controller.list[index]}');
},
itemCount: controller.list.length,
);
будет выполнятьRxInterface.proxy?.addListener(subject);
, это будет чудоRxList
а такжеObx
связаны.
@override
E operator [](int index) {
return value[index];
}
@override
int get length => value.length;
@override
@protected
List<E> get value {
RxInterface.proxy?.addListener(subject);
return _value;
}
Далее посмотримdebug
Информация о стеке может быть очень ясной, как работает весь процесс.
- Создать прослушиватель
- Буду
RxInterface.proxy
установить как текущий_observer
- обратный вызов построителя, вот-вот сработает
RxList
магия артефакта
- перейти на подписку
RxList
серединаStream
- Формальный мониторинг
- когда мы
RxList
вносить изменения, напримерadd
, включить монитор
- последний триггер
_ObxState
середина_updateTree
-
Obx
dispose
при закрытии потока.
@override
void dispose() {
subs.cancel();
_observer.close();
super.dispose();
}
резюме
-
.obs
серия, в том числе базоваяint
,double
,List
и другие инфраструктурные пакеты, а также включает в себяStream
дать уведомление. -
Obx
через паруRxInterface.proxy
настройки (блинDart
Однониточка, такая ароматная! ), обязательноbuilder
в обратном вызове.obs
Только текущийRxInterface.proxy
знак равноObx
, чтобы гарантировать, что текущий.obs
вызовет только соответствующийObx
обновить. -
вам не нужно создавать
SreamController
; вам не нужно создавать по одному для каждой переменнойStreamBuilder
; вам не нужно создавать для каждой переменнойValueNotifier
... одно сказать, это действительно ароматно.
GetxController
часто сGetBuilder
Используется вместе сChangeNotifier
сходство.
class FFController extends GetxController {
List<int> list = <int>[];
void add(int i) {
list.add(i);
update();
}
}
class RxListDemo extends StatelessWidget {
RxListDemo({Key? key}) : super(key: key);
FFController controller = Get.put<FFController>(FFController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: GetBuilder<FFController>(
builder: (FFController controller) {
return ListView.builder(
itemBuilder: (BuildContext b, int index) {
return Text('$index:${controller.list[index]}');
},
itemCount: controller.list.length,
);
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
controller.add(Random().nextInt(100));
},
),
);
}
}
Основного кода ядра не много, принцип прост, пользуйтесьGetInstance
БудуFFController
делать мониторинг и т.д.FFController
update
обновить, когдаGetBuilder
. существуетdispose
Следуйте условному выпуску, когдаFFController
.
class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
with GetStateUpdaterMixin {
T? controller;
bool? _isCreator = false;
VoidCallback? _remove;
Object? _filter;
@override
void initState() {
// _GetBuilderState._currentState = this;
super.initState();
widget.initState?.call(this);
var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);
if (widget.global) {
if (isRegistered) {
if (GetInstance().isPrepared<T>(tag: widget.tag)) {
_isCreator = true;
} else {
_isCreator = false;
}
controller = GetInstance().find<T>(tag: widget.tag);
} else {
controller = widget.init;
_isCreator = true;
GetInstance().put<T>(controller!, tag: widget.tag);
}
} else {
controller = widget.init;
_isCreator = true;
controller?.onStart();
}
if (widget.filter != null) {
_filter = widget.filter!(controller!);
}
_subscribeToController();
}
/// Register to listen Controller's events.
/// It gets a reference to the remove() callback, to delete the
/// setState "link" from the Controller.
void _subscribeToController() {
_remove?.call();
_remove = (widget.id == null)
? controller?.addListener(
_filter != null ? _filterUpdate : getUpdate,
)
: controller?.addListenerId(
widget.id,
_filter != null ? _filterUpdate : getUpdate,
);
}
void _filterUpdate() {
var newFilter = widget.filter!(controller!);
if (newFilter != _filter) {
_filter = newFilter;
getUpdate();
}
}
@override
void dispose() {
super.dispose();
widget.dispose?.call(this);
if (_isCreator! || widget.assignId) {
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
GetInstance().delete<T>(tag: widget.tag);
}
}
_remove?.call();
controller = null;
_isCreator = null;
_remove = null;
_filter = null;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
widget.didChangeDependencies?.call(this);
}
@override
void didUpdateWidget(GetBuilder oldWidget) {
super.didUpdateWidget(oldWidget as GetBuilder<T>);
// to avoid conflicts when modifying a "grouped" id list.
if (oldWidget.id != widget.id) {
_subscribeToController();
}
widget.didUpdateWidget?.call(oldWidget, this);
}
@override
Widget build(BuildContext context) {
// return _InheritedGetxController<T>(
// model: controller,
// child: widget.builder(controller),
// );
return widget.builder(controller!);
}
}
а такжеGetxController
и некоторые сохранены вGetInstance
Автоматическое освобождение объектов в , снова с намиGexX
тесно связаны с управлением маршрутизацией.
управление маршрутом
Flutter
серединаcontext
очень важная вещь, многоapi
неотделимы от него. У вас, должно быть, была эта мысль, надеюсь, что безcontext
В случае использования маршрутизацииSnackBars
, Dialogs
, BottomSheets
.
Вообще-то, нетcontext
Метод маршрутизации на самом деле очень прост.
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey(debugLabel: 'navigate');
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: App.navigatorKey,
home: RxListDemo(),
);
}
}
Вам нужно только использовать
App.navigatorKey.currentState.pushNamed('/home');
И все этоGexX
Это все упаковано для вас, вам просто нужно положитьMaterialApp
заменитьGetMaterialApp
.
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
Это нужно только при использовании
Get.to(NextScreen());
Get.back();
Get.back(result: 'success');
Get.toNamed("/NextScreen");
Get.toNamed("/NextScreen", arguments: 'Get is the best');
// 获取参数
print(Get.arguments);
Конечно,GexX
Маршрутизация — это гораздо больше, чем то, что вы видите, ее основная задача — соединить весьGexX
вселенная.
GetPage
GetPage
унаследовано отPage<T>
,а такжеPage<T>
унаследовано отRouteSettings
, Это описание страницы. пройти черезGetPage
собраны вGetPageRoute
.
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => MyHomePage(),
),
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
],
)
GetPageRoute
MaterialPageRoute
а такжеCupertinoPageRoute
Каждый должен быть знаком сGetPageRoute
и они одно.
class GetPageRoute<T> extends PageRoute<T>
with GetPageRouteTransitionMixin<T>, PageRouteReportMixin {
}
Отличие в том, что у него другие задачи, он будетinstall
(Вы можете просто интерпретировать это какpush
) а такжеdispose
(Вы можете просто интерпретировать это какpop
) ставить в известностьRouterReportManager
.
mixin PageRouteReportMixin<T> on Route<T> {
@override
void install() {
super.install();
RouterReportManager.reportCurrentRoute(this);
}
@override
void dispose() {
super.dispose();
RouterReportManager.reportRouteDispose(this);
}
}
а такжеRouterReportManager
Одной из задач является управление различными экземплярами, которые мы зарегистрировали на текущей странице.Ниже приведен важный код.
class RouterReportManager<T> {
static final Map<Route?, List<String>> _routesKey = {};
static final Map<Route?, HashSet<Function>> _routesByCreate = {};
static Route? _current;
// ignore: use_setters_to_change_properties
static void reportCurrentRoute(Route newRoute) {
_current = newRoute;
}
/// Links a Class instance [S] (or [tag]) to the current route.
/// Requires usage of `GetMaterialApp`.
static void reportDependencyLinkedToRoute(String depedencyKey) {
if (_current == null) return;
if (_routesKey.containsKey(_current)) {
_routesKey[_current!]!.add(depedencyKey);
} else {
_routesKey[_current] = <String>[depedencyKey];
}
}
static void reportRouteDispose(Route disposed) {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
_removeDependencyByRoute(disposed);
});
}
}
-
push
триггер новой страницыreportCurrentRoute
, установите текущий_current
. - при вызове на текущей странице
Get.put
будет вызван, когдаreportDependencyLinkedToRoute
метод, сохраните его. -
pop
Запускается, когда страницаreportRouteDispose
По некоторым правилам экземпляр освобождается.
FFRoute
В реальной эксплуатации к следующим 2 пунктам я никак не могу привыкнуть.
- установить вручную
getPages
собирать - Потому что только через
Get.arguments
Получение параметров, слабая типизация неудобна.
Для этой цели я добавилFFRoute
а такжеGetX
结合的例子。 (FFRoute
это инструмент, который использует аннотации для создания маршрутов)
ff_annotation_route/example_getx at master · fluttercandies/ff_annotation_route (github.com)
- На самом деле, вам просто нужно
onGenerateRoute
Обратный звонок Генерал-лейтенантFFRouteSettings
преобразовать в соответствующийGetPageRoute
.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: Routes.fluttercandiesMainpage.name,
onGenerateRoute: (RouteSettings settings) {
FFRouteSettings ffRouteSettings = getRouteSettings(
name: settings.name!,
arguments: settings.arguments as Map<String, dynamic>?,
notFoundPageBuilder: () => Scaffold(
appBar: AppBar(),
body: const Center(
child: Text('not find page'),
),
),
);
Bindings? binding;
if (ffRouteSettings.codes != null) {
binding = ffRouteSettings.codes!['binding'] as Bindings?;
}
Transition? transition;
bool opaque = true;
if (ffRouteSettings.pageRouteType != null) {
switch (ffRouteSettings.pageRouteType) {
case PageRouteType.cupertino:
transition = Transition.cupertino;
break;
case PageRouteType.material:
transition = Transition.downToUp;
break;
case PageRouteType.transparent:
opaque = false;
break;
default:
}
}
return GetPageRoute(
binding: binding,
opaque: opaque,
settings: ffRouteSettings,
transition: transition,
page: () => ffRouteSettings.builder(),
);
},
);
}
}
- При использовании записи
Get.toNamed(Routes.itemPage.name,arguments: Routes.itemPage.d(index: index));
Суммировать
Это не введение в использованиеGetX
Статья представляет собой простое понимание с точки зрения исходного кодаGetX
Принцип трех функций — не что иное, как это.
преимущество
-
Простой в использовании
если ты прав
Flutter
Принцип понятен,GetX
Абсолютный убийца, он может значительно сократить время, которое вы тратите на написание кода. -
Многофункциональный
Помимо трех основных функций управления состоянием, управления зависимостями и управления маршрутизацией, он также включает в себя интернационализацию, темы, сетевые запросы и т. д. с ощущением семейного ведра.
недостаток
-
Простой в использовании
В этом его преимущество и его недостаток. он скрывает
Flutter
Самый основной принцип. Это может быть удовольствие для новичка для использования, но трудно устранить неполадки, если вы столкнулись с проблемами. Очевидное явление заключается в том, что многие новички приходят к группе, чтобы спросить,GetX
Как это не работает, это действительно расстраивает после долгого времени. -
Многофункциональный
Слишком много инкапсуляций, нужно учитывать, насколько большим будет влияние, если библиотека перестанет обновляться. Несмотря на официальное обещание следующего, я думаю
always
Это слово следует использовать с осторожностью.
-
слишком преувеличенное описание
Некоторые описания слишком преувеличены, что также приводит к
GetX
одеялоFlutter Team
отменитьFlutter Favorite
одна из причин.
Эпилог
GetX
Это трехсторонняя библиотека феноменального уровня, способ ее использования полностью зависит от вашей собственной ситуации. Новичкам рекомендуется использовать трехсторонние рамки, не придумывая, они будут мешать вашему пониманию.Flutter
понимание принципов. На самом деле зачастую в технологиях нет ничего плохого, просто люди, которые их используют, разные.
наконец надетьGetX
Официальная китайская документация:
ЛюбовьFlutter
,Любовь糖果
,Добро пожаловатьFlutter Candies, чтобы вместе производить милые конфеты FlutterГруппа QQ: 181398081
надевать в последнюю очередьFlutter CandiesВся семейная бочка, действительно ароматная.