предисловие
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
-
Obxdisposeпри закрытии потока.
@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Вся семейная бочка, действительно ароматная.