The method 'salvar' was called on null - flutter

I have a problem with that situation. Can you help me ? I'm taking this error message.
Exception has occurred.
I want save a thing in a list but show:
The following NoSuchMethodError was thrown while handling a gesture:
The method 'salvar' was called on null.
Receiver: null
Tried calling: salvar(Instance of 'Tarefa')
THE CODE:
class TarefaScreen extends StatefulWidget {
#override
_TarefaScreenState createState() => _TarefaScreenState();
}
class _TarefaScreenState extends State<TarefaScreen> {
final _formKey = GlobalKey<FormState>();
TarefaService _tarefaService;
String _titulo;
String _descricao;
DateTime _dataHora;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
_save() {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
Tarefa _tarefa = Tarefa(
titulo: this._titulo,
descricao: this._descricao,
dataHora: this._dataHora);
this._tarefaService.salvar(_tarefa).then((value) {
showInfo("Tarefa adicionada");
Navigator.of(context).pop();
}
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Adicionar Tarefa")
,
),
body: Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
children: <Widget>[...
Padding(
padding: const EdgeInsets.only(top: 30, left: 10, right: 10, bottom: 5),
child: RaisedButton(
child: Text("Enviar"),
onPressed: () {
this._save();
},
),
),
],
),
),
);
}
}
And I define addItem here;
class TarefaService {
final TarefaStore tarefaStore;
TarefaService(this.tarefaStore);
Future<List<Tarefa>> buscarTarefas() {
return Future.value(tarefaStore.tarefas);
}
Future<Tarefa> salvar(Tarefa atividade){
tarefaStore.adicionarTarefa(atividade);
return Future.value(atividade);
}
void dispose(){
}
}
Please, help me

You are trying to call the method on the object which is not instantiated so its null in this._tarefaService.salvar(_tarefa). You need to instantiate _tarefaService. You can do it in init()
_tarefaService = new TarefaService(tarefaStore);

Related

LateInitializationError in initialization of Stateful Widget

In my mobile application, I am initializing a Stateful widget from another widget but I always get an exception
[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception:
LateInitializationError: Field '_customAppLoaderState#64195267' has
not been initialized
Below is the code for custom_loader.dart
import 'package:SMedoApp/util/app_textstyles.dart';
import 'package:SMedoApp/util/color_constants.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CustomAppLoader extends StatefulWidget {
// const CustomAppLoader({Key? key}) : super(key: key);
late final _CustomAppLoaderState _customAppLoaderState;
#override
State<CustomAppLoader> createState() {
_customAppLoaderState=_CustomAppLoaderState();
return _customAppLoaderState;
}
void setLoaderVisible(bool _visibility){
_customAppLoaderState.setVisibility(_visibility);
}
void setProgressPerc(double progress){
_customAppLoaderState.setProgressValue(progress: progress);
}
void setCancelToken(CancelToken cancelToken) {
_customAppLoaderState.setCancelToken(cancelToken: cancelToken);
}
}
class _CustomAppLoaderState extends State<CustomAppLoader> {
bool isLoaderVisible=false;
double _progress=0.0;
CancelToken? _cancelToken;
bool isCancelButtonVisible=false;
#override
Widget build(BuildContext context) {
return Visibility(
visible: isLoaderVisible,
child: Center(
child: Container(
color: ColorConstants.black.withOpacity(0.8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SpinKitWave(
size: 50,
color: ColorConstants.white,
type: SpinKitWaveType.start
),
SizedBox(
height: 50,
),
Container(
width: 200,
child: LinearProgressIndicator(
backgroundColor: ColorConstants.white,
valueColor: new AlwaysStoppedAnimation<Color>(ColorConstants.facebook_blue),
value: _progress,
minHeight: 2,
),
),
SizedBox(
height: 10,
),
Visibility(
visible: isCancelButtonVisible,
child: TextButton(onPressed: (){
_cancelToken?.cancel();
if(_cancelToken!.isCancelled)
Navigator.pop(context);
}, child: Text(AppLocalizations.of(context)!.cancel, style: AppTextStyle.whiteOnBlackSmallWhite(context),), ),
)
],
)),
),
);
}
void setVisibility(bool _visibility){
setState(() {
isLoaderVisible=_visibility;
});
}
void setProgressValue({required double progress}) {
setState(() {
_progress=progress;
}
);
}
void setCancelToken({required CancelToken cancelToken}) {
setState(() {
_cancelToken=cancelToken;
isCancelButtonVisible=true;
});
}
}
And this is how I invoke custom_loader from another widget
CustomAppLoader loader=CustomAppLoader();
loader.setProgressPerc(0.25);
Where am I going wrong? (I am new to flutter/ dart).
createState() is not called yet on initialization of the CustomAppLoader, so when you call setProgressPerc the state doesn't exist yet. It's also not really common to save the state in a variable and using it like that.
My IDE also actually suggest that you shouldn't do any logic in the createState():

Implement setstat and bind variables

I'm trying to get and display 2 variable values from another dart file, ("int myId" and "String myMenu") , these variables are updated with every "onTap" widget, my code works, but only if i do a "hot reload", i think that i need to put a "setstate" somewhere, but i'm having difficulty to implement it.
I think the problem is there, my widget text returns "null" to me, but if I hit the menu button and do a "hot reload", it's ok.
displayText.dart
import 'package:flutter/material.dart';
import './menu.dart';
class display extends StatefulWidget {
int myId;
String myMenu;
display(this.myId, this.myMenu);
#override
_displayState createState() => _displayState();
}
class _displayState extends State<display> {
Future myVarUsed() async {
//Each press on the button return the value
setState(() {
print('myIdDsiplay: ${widget.myId}'); // null
print('myMenuDisplay : ${widget.myMenu}'); // null
});
}
#override
void initState() {
super.initState();
myVarUsed();
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 250,
width: 250,
child: Row(
children: [
Text('My ID is : ${widget.myId}'),
Text('My menu is : ${widget.myMenu}'),
],
),
);
}
}
This file contains the menu inside a scrollbar, each button return the ID and the name (of the button) and store it in 2 variable ("int myId" and "String myMenu") that i want to pass.
menu.dart
import 'package:flutter/material.dart';
import './mylist.dart';
import './displayText.dart';
class Menu extends StatefulWidget {
static int myId;
static String myMenu;
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Container scrollList() {
final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);
return Container(
color: Colors.red,
height: 90,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: listdata.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Container(
child: gestureDetector_Ontap(index),
);
},
),
);
}
GestureDetector gestureDetector_Ontap(int index) {
return GestureDetector(
onTap: () {
Menu.myId = listdata[index].id;
Menu.myMenu = listdata[index].menuObj;
display(Menu.myId, Menu.myMenu);
print('myIDMenu ${Menu.myId}');
print('myMenuMenu ${Menu.myMenu}');
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'${listdata[index].menuObj}',
),
),
);
}
Widget build(BuildContext context) {
return Container(
child: scrollList(),
);
}
}
This file contains my list and his class
mylist.dart
class listModel {
int id;
String menuObj;
listModel(this.id, this.menuObj);
}
List listdata = [
listModel(0, 'Menu01'),
listModel(1, 'Menu02'),
listModel(2, 'Menu03'),
listModel(3, 'Menu04'),
listModel(4, 'Menu05')
];
And the container
main.dart
import 'package:flutter/material.dart';
import './menu.dart';
import './displayText.dart';
import './mylist.dart';
void main() {
runApp(MyHomePage());
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Column(
children: <Widget>[
Menu(),
display(Menu.myId, Menu.myMenu),
],
),
),
),
);
}
}
The problem
You're defining Menu this way:
class Menu extends StatefulWidget {
static int myId;
static String myMenu;
#override
_MenuState createState() => _MenuState();
}
When your app starts, myId and myMenu are uninitialized variables, therefore they're implicitely set to null.
Inside _MyHomePageState, you call
display(Menu.myId, Menu.myMenu)
Since you haven't initialized Menu.myId and Menu.myMenu yet, they're still null.
When you tap the GestureDetector, you initialize Menu.myId and Menu.myMenu this way:
Menu.myId = listdata[index].id;
Menu.myMenu = listdata[index].menuObj;
display(Menu.myId, Menu.myMenu);
print('myIDMenu ${Menu.myId}');
print('myMenuMenu ${Menu.myMenu}');
Now, Menu.myId and Menu.myMenu are defined to non-null values. However, this will not update the Container's display(Menu.myId, Menu.myMenu), so they'll still be null, you need to update it by yourself.
The solution
I've added comments through the code, pointing a better approach:
import 'package:flutter/material.dart';
// Avoid displaying the warning "Name types using UpperCamelCase."
class Display extends StatefulWidget {
// Make these fields final and the constructor const
final int myId;
final String myMenu;
const Display(this.myId, this.myMenu);
#override
_DisplayState createState() => _DisplayState();
}
// Avoid displaying the warning "Name types using UpperCamelCase."
class _DisplayState extends State<Display> {
// You don't need this Future nor this initState
//
// Future myVarUsed() async {
// setState(() {
// print('myIdDsiplay: ${widget.myId}'); // null
// print('myMenuDisplay : ${widget.myMenu}'); // null
// });
// }
//
// #override
// void initState() {
// super.initState();
// myVarUsed();
// }
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 250,
width: 250,
child: Row(
children: [
Text('My ID is : ${widget.myId}'),
Text('My menu is : ${widget.myMenu}'),
],
),
);
}
}
class Menu extends StatefulWidget {
// Avoid using mutable static fields
// static int myId;
// static String myMenu;
// To simplify, you can add a onChanged callback to
// be triggered whenever you change `myId` and `myMenu`
final void Function(int myId, String myMenu) onChanged;
const Menu({this.onChanged});
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Container scrollList() {
final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);
return Container(
color: Colors.red,
height: 90,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: listdata.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Container(
child: gestureDetectorOntap(index),
);
},
),
);
}
// Avoid displaying the warning "Name non-constant identifiers using lowerCamelCase."
GestureDetector gestureDetectorOntap(int index) {
return GestureDetector(
onTap: () {
// Make these local variables
int myId = listdata[index].id;
String myMenu = listdata[index].menuObj;
// Call the `onChanged` callback
widget.onChanged(myId, myMenu);
// This widget is being thrown away
// display(Menu.myId, Menu.myMenu);
print('myIDMenu $myId');
print('myMenuMenu $myMenu');
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'${listdata[index].menuObj}',
),
),
);
}
Widget build(BuildContext context) {
return Container(
child: scrollList(),
);
}
}
// Avoid the warning "Name types using UpperCamelCase."
class ListModel {
// You can make these fields final and the constructor const
final int id;
final String menuObj;
const ListModel(this.id, this.menuObj);
}
// You can make this list const to avoid modifying it unintentionally later
const List<ListModel> listdata = [
ListModel(0, 'Menu01'),
ListModel(1, 'Menu02'),
ListModel(2, 'Menu03'),
ListModel(3, 'Menu04'),
ListModel(4, 'Menu05')
];
void main() {
runApp(MyHomePage());
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Create fields to store the current `myId` and current `myMenu`
int myId;
String myMenu;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Column(
children: <Widget>[
// Add the `onChanged` callback here, updating this widget state
Menu(
onChanged: (newMyId, newMyMenu) {
setState(() {
myId = newMyId;
myMenu = newMyMenu;
});
}
),
// Access the current values here
Display(myId, myMenu),
],
),
),
),
);
}
}

implementing bloc pattern with API cause an exception "type 'Future<dynamic>' is not a subtype of type 'Widget?'"

I am trying to implement bloc pattern in which I am using a repository class which consist all the methods which makes the calls with the API. On the other side I am implementing BlocBuilder to render the view based on bloc state however i am getting this error BlocBuilder<VehiclesBloc, VehiclesState>(dirty, dependencies: [_LocalizationsScope-[GlobalKey#df8d0]], state: _BlocBuilderBaseState<VehiclesBloc, VehiclesState>#dba40):
type 'Future' is not a subtype of type 'Widget?'
I am really not sure where the issues comes from. here are some snippets of the code.
this is the bloc class which causes the error
class VehiclesBloc extends Bloc<VehiclesEvent,VehiclesState>{
VehiclesBloc(VehiclesState initialState) : super(initialState);
#override
Stream<VehiclesState> mapEventToState(VehiclesEvent event) async* {
// TODO: implement mapEventToState
if(event is LoadVehiclesList){
yield* mapLoadEventToState(event);
}
}
Stream<VehiclesState> mapLoadEventToState(LoadVehiclesList event) async* {
if(event is LoadVehiclesList){
var response = await VehiclesService().getAll();
if(response.IsSuccess){
yield VehiclesLoaded(response.Data);
}else{
yield VehiclesLoadingFailed(response.ErrorList.toString());
}
}else{
yield VehiclesLoading();
}
}
}
here is the Statefull widget which implements the Bloc Builder
class VehicleList extends StatefulWidget {
const VehicleList({Key key}) : super(key: key);
static const String routeName = "/VehicleList";
//final ScrollController scrollController;
#override
_VehicleListState createState() => _VehicleListState();
}
class _VehicleListState extends State<VehicleList> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
VehiclesBloc vehiclesBloc =
VehiclesBloc(VehiclesLoading())..add(LoadVehiclesList());
#override
void initState() {
// TODO: implement initState
super.initState();
//VehiclesService().getAll();
}
#override
void dispose() {
// TODO: implement dispose
vehiclesBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
final isRtl = context.locale.languageCode == "ar";
return Scaffold(
key: _scaffoldKey,
backgroundColor: kBackgroundColor,
drawer: SideNavigationDrawer(),
body: Container(
child: Column(
children: [
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
onPressed: () {
_scaffoldKey.currentState.openDrawer();
},
icon: Icon(
Icons.menu,
size: 35,
color: Colors.black,
),
)
],
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
BlocBuilder<VehiclesBloc,VehiclesState>(
builder: (context, state) {
if (state is VehiclesLoaded) {
// return BuildListVehicle(state.lsVehicle);
return Center();
} else if (state is VehiclesLoadingFailed) {
return Center(
child: CustomErrorWidget(),
);
} else {
return Center(
child: LoadingDialog.showLoadingDialog(context,
text: ""),
);
}
},
cubit: vehiclesBloc,
),
],
),
),
)
],
),
));
}
I think this code part causes the problem:
return Center(
child: LoadingDialog.showLoadingDialog(context,text: ""),
);
Possibly, LoadingDialog.showLoadingDialog does not return a Widget but is just a function that returns Future.
For side effects (e.g. you want to show the dialog), you should use listeners instead of executing such code inside the build method. Instead of BlocBuilder, just use BlocConsumer and add the listener:
BlocConsumer<VehiclesBloc,VehiclesState>(
listener: (context, state) {
if (state is {your loading state}) {
LoadingDialog.showLoadingDialog(context, text: "");
}
},
builder: ...,
),
Some more insights about your code:
Instead of creating BLoC as a variable in your stateful widget, use BlocProvider that would handle create/dispose part of your BLoC.
Yield the VehiclesLoading state before loading the data and not just as an "else" case. This way you could handle the loading behaviour easily in your UI.
To fix the above issues, just follow the documentation: https://bloclibrary.dev/

Flutter Provider calls Element.updata (widget) to report an error

Want to imitate the Android fragment switching effect, dynamically update the FrameLayout layout through the fragment. However, when using provider in a project, an exception occurs when calling element. Updata (covariant widget, newwidget).
Exception caught==========
The following assertion was thrown while handling a gesture:
'package:flutter/src/widgets/framework.dart': Failed assertion: line 3439 pos 7: '_lifecycleState == _ElementLifecycle.active
&& widget != null
&& newWidget != null
&& newWidget != widget
&& depth != null
&& Widget.canUpdate(widget, newWidget)': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.md
When the exception was thrown, this was the stack:
#2 Element.update (package:flutter/src/widgets/framework.dart:3439:7)
#3 StatefulElement.update (package:flutter/src/widgets/framework.dart:4751:11)
#4 _HomePageState.itemWidget.<anonymous closure>.<anonymous closure> (package:flutter_app/a/ui/page/home_page.dart:192:25)
#5 BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2473:15)
#6 _HomePageState.itemWidget.<anonymous closure> (package:flutter_app/a/ui/page/home_page.dart:191:27)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#2d601
debugOwner: GestureDetector
state: ready
won arena
finalPosition: Offset(120.0, 195.0)
finalLocalPosition: Offset(40.0, 33.5)
button: 1
sent tap down
The simplified code is as follows (can be run directly).
Click the top button to switch the widget
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
State createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
TabController tabController;
Widget currentWidget;
var pageMap = new Map();
#override
Widget build(BuildContext context) {
return ProviderWidget(
model: SubjectModel(),
onModelReady: (subjectModel) {
subjectModel.curIndexTitle = 'title1';
subjectModel.getSubjects();
currentWidget = LinkListPage(subjectModel.curSubject);
pageMap['title1'] = currentWidget;
},
builder: (context, subjectModel, child) {
return DefaultTabController(
length: subjectModel.subjectList.length,
initialIndex: 0,
child: Builder(
builder: (context) {
if (tabController == null) {
tabController = DefaultTabController.of(context);
tabController.addListener(() {});
}
return Scaffold(
appBar: AppBar(
title: Row(
children: [
Text("${subjectModel.curIndexTitle}"),
RaisedButton(
child: Text('title1'),
onPressed: () {
changeWidget(
subjectModel, subjectModel.subjectList[0]);
}),
RaisedButton(
child: Text('title2'),
onPressed: () {
changeWidget(
subjectModel, subjectModel.subjectList[1]);
}),
RaisedButton(
child: Text('title3'),
onPressed: () {
changeWidget(
subjectModel, subjectModel.subjectList[2]);
}),
],
),
),
body: Center(
child: null != currentWidget
? currentWidget
: CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
)),
);
},
));
});
}
changeWidget(SubjectModel model, Subject subject) {
if (model.curIndexTitle == subject.nameCn) return;
model.changeSubject(subject);
Element e = findChild(context as Element, currentWidget);
if (e != null) {
if (pageMap.containsKey(subject.nameCn)) {
currentWidget = pageMap[subject.nameCn];
} else {
currentWidget = SubjectLinksPage(subject);
}
e.owner.lockState(() {
e.update(currentWidget);
});
}
}
static Element findChild(Element e, Widget w) {
Element child;
void visit(Element element) {
if (w == element.widget)
child = element;
else
element.visitChildren(visit);
}
visit(e);
return child;
}
}
enum SubjectType { HOT, NEW }
//subject_page
class SubjectLinksPage extends StatefulWidget {
final Subject _subject;
SubjectLinksPage(this._subject);
#override
_SubjectLinksPageState createState() => _SubjectLinksPageState();
}
class _SubjectLinksPageState extends State<SubjectLinksPage>
with SingleTickerProviderStateMixin {
TabController _tabController;
final arr = ["hot", "new"];
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: arr.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
child: Container(
child: Center(
child: _appBarView(),
),
),
),
),
body: TabBarView(
controller: _tabController,
children: arr.map((e) {
print(widget._subject.nameCn);
return Center(
child: LinkListPage(widget._subject,
subjectType: e == arr[0] ? SubjectType.HOT : SubjectType.NEW),
);
}).toList(),
),
);
}
Widget _appBarView() {
return TabBar(
tabs: arr.map((e) {
return Tab(
child: Text(e),
);
}).toList(),
controller: _tabController,
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.tab,
isScrollable: true,
labelColor: Colors.white,
unselectedLabelColor: Colors.grey[400],
indicatorWeight: 4.0,
labelStyle: TextStyle(height: 2));
}
}
//link_page
class LinkListPage extends StatefulWidget {
final Subject subject;
final SubjectType subjectType;
LinkListPage(this.subject, {this.subjectType});
#override
State createState() => _LinkListPageState();
}
class _LinkListPageState extends State<LinkListPage> {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
return ProviderWidget(
model: LinksModel(widget.subject),
onModelReady: (model) {},
builder: (context, linksModel, child) {
return Text("${widget.subject.nameCn}----${widget.subjectType}");
});
}
}
//model
class SubjectModel with ChangeNotifier {
Subject curSubject;
String curIndexTitle;
List subjectList = [];
changeSubject(Subject value) async {
curSubject = value;
curIndexTitle = value.nameCn;
notifyListeners();
}
getSubjects() {
try {
subjectList.add(new Subject.init(0, "hot", "title1", "hot"));
subjectList.add(new Subject.init(1, "news", "title2", "r/news"));
subjectList.add(new Subject.init(2, "scoff", "title3", "r/scoff"));
curSubject = subjectList[0];
} catch (e, s) {}
notifyListeners();
}
}
class LinksModel with ChangeNotifier {
final Subject subject;
LinksModel(this.subject);
}
//provider
class ProviderWidget<T extends ChangeNotifier> extends StatefulWidget {
final ValueWidgetBuilder<T> builder;
final T model;
final Widget child;
final Function(T model) onModelReady;
final bool autoDispose;
ProviderWidget(
{Key key,
#required this.model,
#required this.builder,
this.child,
this.onModelReady,
this.autoDispose: true})
: super(key: key);
#override
_ProviderWidgetState<T> createState() => _ProviderWidgetState<T>();
}
class _ProviderWidgetState<T extends ChangeNotifier>
extends State<ProviderWidget<T>> {
T model;
#override
void initState() {
model = widget.model;
widget.onModelReady?.call(model);
super.initState();
}
#override
void dispose() {
if (widget.autoDispose) model.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: model,
child: Consumer<T>(
builder: widget.builder,
child: widget.child,
),
);
}
}
class Subject {
int id;
String name;
String uri;
String nameCn;
Subject.init(
this.id,
this.name,
this.nameCn,
this.uri,
);
}

Calling a function after Widget build(BuildContext context)

I am into flutter to port my android app from java. One thing that is evident in flutter is widgets. Now my biggest obstacle to make my app work as it was on android is starting an async task to request data from the server. I have a custom progress dialog that can be shown or hidden.
class MySelection extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MySelectionState();
}
}
class MySelectionState extends State<MySelection> {
final globalKey = new GlobalKey<ScaffoldState>();
ProgressDialog progressDialog = ProgressDialog.getProgressDialog("Loading books ...");
List<Book> books;
void requestData() async {
EventObject eventObject = await getBooks();
books = eventObject.object;
populateData();
}
#override
Widget build(BuildContext context) {
if (books == null) {
books = List<Book>();
requestData();
}
var appBar = AppBar();
return Scaffold(
appBar: AppBar(
title: Text('Set up your Collection'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
books = List<Book>();
requestData();
},
),
],
),
body: SingleChildScrollView(
child: Stack(
Container(
height: (MediaQuery.of(context).size.height - (appBar.preferredSize.height * 2)),
padding: const EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.only(top: 50.0),
child: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: books.length,
itemBuilder: bookListView,
),
),
Container(
height: (MediaQuery.of(context).size.height),
padding: const EdgeInsets.symmetric(horizontal: 10),
child: progressDialog,
),
],
),
),
}
}
Now, this code works well when I don't call the progress dialog unlike when I try to do that by calling my progressdialog widget.
if (books == null) {
progressDialog.showProgress();
books = List<Book>();
requestData();
}
It throws the error that
The method 'showProgress' was called on null. Receiver: null Tried
calling: showProgress()
Of course, the reason is that I am calling this before its widget is even created. Now my question is how can I do this because I can't afford to put a button for the user to click. I just want this to work on its own once the user is on this particular screen.
import 'package:flutter/scheduler.dart';
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
// add your code which you want to execute after your build is complete
});
}
Thanks.