我對 Flutter 比較陌生,在這個應用程式中,我正在制作一個多選項卡清單表單,該表單是從我服務器中的 API 獲取的 JSON 構建的。
為了存盤這些資訊,我使用了一些帶有 Provider 的模型,并在我在預覽螢屏中收到來自服務器的回應后立即更新它們,這基于來自與此處不相關的用戶的輸入。它看起來像這樣:
**The models**
class KitsEnfermagemList with ChangeNotifier {
List kits;
KitsEnfermagemList({required this.kits});
// a bunch of methods
void addOrUpdate(KitEnfermagem newKit) {
if (doesNotExists(newKit)) {
kits.add(newKit);
notifyListeners();
} else {
KitEnfermagem oldKit = find(newKit)[0];
oldKit.update(newKit);
}
}
void updateAllFromJson(List<dynamic> json) {
if (kits.isNotEmpty) {
for (int i = 0; i < json.length; i ) {
var newKit = KitEnfermagem.fromJson(json[i]);
addOrUpdate(newKit);
}
} else {
addMultiple(json.map((kit) => KitEnfermagem.fromJson(kit)).toList());
}
return;
}
class KitEnfermagem with ChangeNotifier {
String? id;
String? compartimento;
int? tipoVeiculo;
List<ItemPerKit>? itens;
KitEnfermagem({
this.id,
this.compartimento,
this.tipoVeiculo,
this.itens,
});
void update(KitEnfermagem newKit) {
compartimento = newKit.compartimento;
tipoVeiculo = newKit.tipoVeiculo;
itens = newKit.itens;
notifyListeners();
}
}
我如何在我的主要功能中設定我的 ChangeNotifierProvider:
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return MultiProvider(
providers: [
ChangeNotifierProvider<Veiculo>(create: (ctx) => Veiculo()),
ChangeNotifierProvider<KitEnfermagem>(create: (ctx) => KitEnfermagem()),
ChangeNotifierProvider<KitsEnfermagemList>(
create: (ctx) => KitsEnfermagemList(kits: [])),
],
child: MaterialApp(...);
最后是我如何修改我的 KitsEnfermagemList 類,其中包含我用來呈現表單的資料:
// *I'm instantiating this provider on my build(context) function and passing it as a parameter of _submitForm function, just to be clear.*
final kitsEnfermagemList = Provider.of<KitsEnfermagemList>(context);
_submitForm(KitsEnfermagemList kitsEnfermagemList) async {
// do stuff
try {
var responseJson = await _apiService.get(_endpoint);
setState(() {
_isLoading = false;
});
**kitsEnfermagemList.updateAllFromJson(responseJson);
Navigator.of(context)
.pushReplacementNamed(Routes.checklistEnfermagemForm);**
} on... // handling errors
}
我在動態定義需要為表單呈現的選項卡數量時遇到了一些麻煩,因此我嘗試使用 Provider 作為狀態管理器并在我的 TabController 之前進行實體化,為此我覆寫了 didChangeDependencies 方法。
class _ChecklistEnfermagemFormViewState
extends State<ChecklistEnfermagemFormView>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
int currentTab = 0;
final Map<String, dynamic> _formData = {'checklists_kit': []};
late TabController _tabController;
late Veiculo veiculo;
late KitsEnfermagemList kitsEnfermagemList;
late int tabsLength;
@override
void didChangeDependencies() {
super.didChangeDependencies();
veiculo = Provider.of<Veiculo>(context);
kitsEnfermagemList = Provider.of<KitsEnfermagemList>(context);
assert(kitsEnfermagemList.isNotEmpty == true);
tabsLength = kitsEnfermagemList.length viewModel.othersTabsLength;
kitsEnfermagemList.kits
.map((kit) => _formData['checklists_kit'].add(ChecklistKit().toJson()));
_tabController = TabController(length: tabsLength, vsync: this);
}
TabBarView _buildTabBarView() {
return TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: _tabController,
children: <Widget>[
// dynamically generating each tab
],
),
);
return CustomWillPopScope(
child: Scaffold(
appBar: appBar,
body: AppBackground(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _buildTabBarView(),
),
),
);
}
}
我遇到的問題是在我渲染上一個小部件的第一刻:
======== Exception caught by widgets library =======================================================
The following assertion was thrown while rebuilding dirty elements:
_ChecklistEnfermagemFormViewState is a SingleTickerProviderStateMixin but multiple tickers were created.
A SingleTickerProviderStateMixin can only be used as a TickerProvider once.
If a State is used for multiple AnimationController objects, or if it is passed to other objects and those objects might use it more than one time in total, then instead of mixing in a SingleTickerProviderStateMixin, use a regular TickerProviderStateMixin.
The relevant error-causing widget was:
ChecklistEnfermagemFormView ChecklistEnfermagemFormView:file:///mnt/5dfbbeb6-7a4a-4a3a-9f0d-dd39eb411d55/Projetos/_mobile/dirigir_assessoria/lib/main.dart:110:21
When the exception was thrown, this was the stack:
#0 SingleTickerProviderStateMixin.createTicker.<anonymous closure> (package:flutter/src/widgets/ticker_provider.dart:188:7)
#1 SingleTickerProviderStateMixin.createTicker (package:flutter/src/widgets/ticker_provider.dart:197:6)
#2 new AnimationController.unbounded (package:flutter/src/animation/animation_controller.dart:279:21)
#3 new TabController (package:flutter/src/material/tab_controller.dart:110:50)
#4 _ChecklistEnfermagemFormViewState.didChangeDependencies (package:dirigir_assessoria/screens/enfermagem/checklist_enfermagem_form_screen.dart:159:22)
#5 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4925:13)
#6 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#7 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#8 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#9 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#10 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#11 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#12 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
#16 _invoke (dart:ui/hooks.dart:151:10)
#17 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#18 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
The element being rebuilt at the time was index 3 of 7: ChecklistEnfermagemFormView
dirty
dependencies: [_InheritedTheme, MediaQuery, _InheritedProviderScope<KitsEnfermagemList>, _LocalizationsScope-[GlobalKey#a34b4], _InheritedProviderScope<Veiculo>]
state: _ChecklistEnfermagemFormViewState#53df3(ticker inactive)
====================================================================================================
======== Exception caught by scheduler library =====================================================
buildScope missed some dirty elements.
The list of dirty elements at the end of the buildScope call was:
: HomeScreen
dependencies: [_InheritedTheme, MediaQuery, _LocalizationsScope-[GlobalKey#a34b4]]
: ChecklistEnfermagemFormView
dirty
dependencies: [_InheritedTheme, MediaQuery, _InheritedProviderScope<KitsEnfermagemList>, _LocalizationsScope-[GlobalKey#a34b4], _InheritedProviderScope<Veiculo>]
state: _ChecklistEnfermagemFormViewState#53df3(ticker inactive)
: Scaffold
dependencies: [Directionality, _InheritedTheme, MediaQuery, _ScaffoldMessengerScope, UnmanagedRestorationScope, _LocalizationsScope-[GlobalKey#a34b4]]
state: ScaffoldState#aa553(tickers: tracking 2 tickers)
: Scaffold
dependencies: [Directionality, _InheritedTheme, MediaQuery, UnmanagedRestorationScope, _ScaffoldMessengerScope, _LocalizationsScope-[GlobalKey#a34b4]]
state: ScaffoldState#07a62(tickers: tracking 2 tickers)
: AppBar
dependencies: [_InheritedTheme, _ScrollNotificationObserverScope, MediaQuery, FlexibleSpaceBarSettings, _ModalScopeStatus, _LocalizationsScope-[GlobalKey#a34b4]]
state: _AppBarState#06d17
: Text
"Checklist da Enfermagem"
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Gaveta 1"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Mala Laringosc?3pio"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Balc?£o/Sal?£o"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Maleta Psicotr?3picos"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Ba?o"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Assinatura"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Arm??rio A"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Gaveta 2"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"R??gua"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Mala de Vias A??reas e Oxigenoterapia"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Mala de Trauma"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Bolsa de Sinais Vitais"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Mala de Procedimento"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Mala de Medica?§?£o e Acesso Venoso"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Gaveta 3"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Arm??rio B"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [MediaQuery, DefaultTextStyle]
: Text
"Fotos"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [DefaultTextStyle, MediaQuery]
: Text
"Inicial"
debugLabel: englishLike labelLarge 2014
inherit: false
size: 14.0
weight: 500
baseline: alphabetic
dependencies: [DefaultTextStyle, MediaQuery]
...
====================================================================================================
我已經嘗試使用 TickerProviderStateMixin 而不是 SingleTickerProviderStateMixin 并且它停止顯示錯誤,但也引入了一些意外行為,例如如果我在跳到下一個后關閉其剩余的鍵盤,則回傳到初始選項卡,所以我的結論是它沒有解決問題,它只是隱藏它......
我在不需要狀態管理的情況下構建了另一種不太動態的表單,并且運行良好。但老實說,這是我第一次嘗試使用 Provider,我覺得我錯過了一些實作規則。任何人都可以幫忙嗎?
uj5u.com熱心網友回復:
替換你SingleTickerProviderStateMixin的TickerProviderStateMixin
這是因為重建選項卡控制器以更改選項卡計數會導致同時出現多個控制器。并且每個控制器都依賴于一個帶有該vsync值的 Ticker。所以應該有多個 Ticker 來支持它。
另一個注意事項:
像這樣使用didChangeDependencies生命周期方法會導致對您的提供者的不必要呼叫。因為當狀態的依賴改變時,這個方法在 AND之后被呼叫。initState如果您正在使用它,因為我猜想避免使用context并希望其中的代碼只執行一次,我建議您像這樣使用它:
bool _isInit = true;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if(_isInit) {
// The following code is executed only once
veiculo = Provider.of<Veiculo>(context);
kitsEnfermagemList = Provider.of<KitsEnfermagemList>(context);
assert(kitsEnfermagemList.isNotEmpty == true);
tabsLength = kitsEnfermagemList.length viewModel.othersTabsLength;
kitsEnfermagemList.kits
.map((kit) => _formData['checklists_kit'].add(ChecklistKit().toJson()));
_tabController = TabController(length: tabsLength, vsync: this);
}
_isInit = false;
}
如果您真的想在每次依賴項更改時執行代碼,那么您可以使用didUpdateWidget生命周期方法。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/467981.html
上一篇:如何在Flutter中使用Getx包將發布請求作為表單資料發出?
下一篇:主體可能正常完成,導致回傳“null”,但回傳型別“State<StatefulWidget>”可能是不可為空的型別
