问题
I am currently having an issue while fetching a Provider' value in
initstate`.
I want to set a default value in dropdown in an Appbar and other parts in body. But I got an error saying dependOnInheritedElement() was called before initstate() in flutter
.
My full code is below
main.dart
import 'package:test_eoil/model/button_data.dart';
import 'package:test_eoil/model/output_data.dart';
import 'screen/screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(providers: [
// ChangeNotifierProvider<ChordData>(create: (context) => ChordData()),
ChangeNotifierProvider<OutputData>(create: (context) => OutputData()),
ChangeNotifierProvider<ButtonData>(create: (context) => ButtonData())
],
child: MaterialApp(
home: Screen(),
),
);
}
}
screen.dart
in screen folder
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_eoil/model/button_data.dart';
import 'package:test_eoil/model/output_data.dart';
class Screen extends StatefulWidget {
@override
_ScreenState createState() => _ScreenState();
}
class _ScreenState extends State<Screen> {
Widget dropdownWidget() {
return DropdownButton<Button>(
items: Provider.of<ButtonData>(context).buttons.map((Button value) {
return new DropdownMenuItem<Button>(
value: value,
child: new Text(value.type.toString()),
);
}).toList(),
onChanged: (Button newValue) {
Provider.of<ButtonData>(context).setSelectedItem(newValue);
},
value: Provider.of<ButtonData>(context).selectedButton,
);
}
@override
void initState() {
Provider.of<ButtonData>(context).selectedButton = Provider.of<ButtonData>(context).buttons.first;
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<OutputData>(
builder: (context, outputData, child) => Scaffold(
appBar: AppBar(
title: Text("${Provider.of<ButtonData>(context).selectedButton}"), // new Text(widget.title), // "${Provider.of<ButtonData>(context).selectedButton.key}"
actions: <Widget>[
dropdownWidget(),
],
),
body: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.centerRight,
padding: new EdgeInsets.symmetric(
vertical: 24.0, horizontal: 12.0),
child: outputData.outputs[0].output3.length > 2
? Text(
'${outputData.outputs[0].output3.substring(1, outputData.outputs[0].output3.length-1)}',
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.bold,
))
: Text('${outputData.outputs[0].output3}',
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.bold,
)),
),
Container(
alignment: Alignment.centerRight,
padding: new EdgeInsets.symmetric(
vertical: 24.0, horizontal: 12.0),
child: outputData.outputs[0].output2.length > 2
? Text(
'${outputData.outputs[0].output2.substring(1, outputData.outputs[0].output2.length-1)}',
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.bold,
))
: Text('${outputData.outputs[0].output2}',
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.bold,
)),
),
Container(
alignment: Alignment.centerRight,
padding: new EdgeInsets.symmetric(
vertical: 24.0, horizontal: 12.0),
child: Text('${outputData.outputs[0].output1}', // outputData.outputs[0].output1
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.bold,
)),
),
],
),
Expanded(
child: new Divider(),
),
Column(children: [
Row(children: [
buildButton("CLEAR"),
buildButton(""),
buildButton("PLAY"),
// buildButton("/")
]),
])
],
)));
}
Widget buildButton(String buttonText) {
return new Expanded(
child: new OutlineButton(
padding: new EdgeInsets.all(30.0),
child: new Text(
buttonText,
style: TextStyle(fontSize: 20.0),
),
onPressed: () => print("test"),
),
);
}
}
button_data.dart
in model folder
import 'package:flutter/foundation.dart';
//import 'dart:collection';
class Button {
final int id;
final String type;
final String numberone;
final String numbertwo;
final String numberthree;
Button({this.id, this.type, this.numberone, this.numbertwo, this.numberthree});
}
class ButtonData extends ChangeNotifier {
List<Button> _buttons = [
Button(
type: "A",
numberone: "1",
numbertwo: "2",
numberthree: "3",
),
Button(
type: "B",
numberone: "A",
numbertwo: "B",
numberthree: "C",
),
];
List<Button> get buttons => _buttons;
Button _selectedButton;
Button get selectedButton => _selectedButton;
set selectedButton(Button button) {
_selectedButton = button;
notifyListeners();
}
void setSelectedItem(Button s) {
_selectedButton = s;
notifyListeners();
}
Button getKey(String value) {
return _buttons
.where((button) => button.type == value).first;
}
String getNumberOne(String value) {
return _buttons
.where((button) => button.type == value)
.map((button) => (button.numberone))
.toString();
}
String getNumberTwo(String value) {
return _buttons
.where((button) => button.type == value)
.map((button) => (button.numbertwo))
.toString();
}
String getNumberThree(String value) {
return _buttons
.where((button) => button.type == value)
.map((button) => (button.numberthree))
.toString();
}
}
output_data.dart
in model folder
import 'package:flutter/foundation.dart';
class Output {
final int id;
String output1;
String output2;
String output3;
Output({this.id, this.output1, this.output2, this.output3});
}
class OutputData extends ChangeNotifier {
List<Output> _outputs = [
Output(output1: 'Hello', output2: 'Hi', output3: 'Nice'),
Output(output1: 'Haha', output2: 'Bye', output3: 'Sad'),
];
List<Output> get outputs {
return _outputs;
}
}
To be honest, I want to make it work without initstate()
if possible(I heard that provider pattern doesn't need stful)
The reason I come up with an initstate()
is this is the only solution (as much as I know) to set the default value in provider.
Hope you guys help me!
Issue is solved by adding Button_data constructor in button_data.dart
.
ButtonData () {
_selectedButton = _buttons.first;
}
回答1:
I also mentioned in my previous answer is that Provider.of(context)
is supposed to be used inside the widget tree, and anything that is outside of the build()
method, is not in the widget tree. But if you still want to use it, then you need to set the listen
parameter to false
.
Like so:
@override
void initState() {
Provider.of<ButtonData>(context, listen: false).selectedButton = Provider.of<ButtonData>(context, listen: false).buttons.first;
super.initState();
}
But as you mentioned in your question, you don't want to use initState
to set the default value. In every programming language, when you declare a variable with a value, that value becomes it's initial / default value, and you can change it later.
Instead of using initState
, you can edit the following in your ButtonData
class.
//Remove this.
List<Button> get buttons => _buttons;
Button _selectedButton;
Button get selectedButton => _selectedButton;
//Instead, Use this.
List<Button> get buttons => _buttons;
Button _selectedButton = _buttons.first;
Button get selectedButton => _selectedButton;
//This will declare the `selectedButton` variable with a default value.
//Happy coding! :)
来源:https://stackoverflow.com/questions/60363665/dependoninheritedelement-was-called-before-initstate-in-flutter