I have 2 screens in my Flutter app: a list of records and a screen for creating and editing records.
If I pass an object to the second screen that means I am going to edit this and if I pass null it means that I am creating a new item. The editing screen is a Stateful widget and I am not sure how to use this approach https://flutter.io/cookbook/navigation/passing-data/ for my case.
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, @required this.recordObject}) : super(key: key);
@override
_RecordPageState createState() => new _RecordPageState();
}
class _RecordPageState extends State<RecordPage> {
@override
Widget build(BuildContext context) {
//.....
}
}
How can I access recordObject inside _RecordPageState ?
To use recordObject in _RecordPageState, you have to just write widget.objectname like below
class _RecordPageState extends State<RecordPage> {
@override
Widget build(BuildContext context) {
.....
widget.recordObject
.....
}
}
You don't need to pass parameters to State using it's constructor. You can easily access these using widget.myField .
class MyRecord extends StatefulWidget {
final String recordName;
const MyRecord(this.recordName);
@override
MyRecordState createState() => MyRecordState();
}
class MyRecordState extends State<MyRecord> {
@override
Widget build(BuildContext context) {
return Text(widget.recordName); // Here you direct access using widget
}
}
Pass your data when you Navigate screen:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MyRecord("WonderWorld")));
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, @required this.recordObject}) : super(key: key);
@override
_RecordPageState createState() => new _RecordPageState(recordObject);
}
class _RecordPageState extends State<RecordPage> {
Record recordObject
_RecordPageState(this. recordObject); //constructor
@override
Widget build(BuildContext context) {. //closure has access
//.....
}
}
example as below:
class nhaphangle extends StatefulWidget {
final String username;
final List<String> dshangle;// = ["1","2"];
const nhaphangle({ Key key, @required this.username,@required this.dshangle }) : super(key: key);
@override
_nhaphangleState createState() => _nhaphangleState();
}
class _nhaphangleState extends State<nhaphangle> {
TextEditingController mspController = TextEditingController();
TextEditingController soluongController = TextEditingController();
final scrollDirection = Axis.vertical;
DateTime Ngaysx = DateTime.now();
ScrollController _scrollController = new ScrollController();
ApiService _apiService;
List<String> titles = [];
@override
void initState() {
super.initState();
_apiService = ApiService();
titles = widget.dshangle; //here var is call and set to
}
Often, you not only want to navigate to a new screen, but also pass data to the screen as well. For example, you might want to pass information about the item that's been tapped.
In this example, create a list of todos. When a todo is tapped, navigate to a new screen (widget) that displays information about the Record. This recipe uses the following steps:
StatefulWidget
. We call it RecordsScreen (for: Display a list of Records).Define a RecordObject class
class RecordsScreen extends StatefulWidget {
List<RecordObject> records;
RecordsScreen({Key key, @required this.records}) : super(key: key);
@override
_RecordsScreenState createState() => _RecordsScreenState();
}
class _RecordsScreenState extends State<RecordsScreen> {
@override
Widget build(BuildContext context) {
widget.records = List<RecordObject>.generate(20,
(i) => RecordObject(
'Record $i',
'A description of what needs to be done for Record $i',
),
);
return Scaffold(
appBar: AppBar(
title: Text('Records'),
),
body: ListView.builder(
itemCount: widget.records.length,
itemBuilder: (context, index) {
return ListTile(
title: Text( widget.records[index].title),
// When a user taps the ListTile, navigate to the DetailScreen.
// Notice that you're not only creating a DetailScreen, you're
// also passing the current todo through to it.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(recordObject: widget.records[index]),
),
);
},
);
},
),
);
}
Create a detail screen - The title of the screen contains the title of the record, and the body of the screen shows the description.
class DetailScreen extends StatefulWidget {
// Declare a field that holds the RecordObject.
final RecordObject recordObject;
// In the constructor, require a RecordObject.
DetailScreen({Key key, @required this.recordObject}) : super(key: key);
@override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
@override
Widget build(BuildContext context) {
// Use the RecordObject to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(widget.recordObject.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(widget.recordObject.description),
),
);
}
}
I have to Navigate back to any one of the screens in the list pages but when I did that my onTap function stops working and navigation stops.
class MyBar extends StatefulWidget {
MyBar({this.pageNumber});
final pageNumber;
static const String id = 'mybar_screen';
@override
_MyBarState createState() => _MyBarState();
}
class _MyBarState extends State<MyBar> {
final List pages = [
NotificationScreen(),
AppointmentScreen(),
RequestBloodScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
var _selectedItemIndex = widget.pageNumber;
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
elevation: 0,
backgroundColor: Colors.white,
unselectedItemColor: Colors.grey.shade700,
selectedItemColor: Color(kAppColor),
selectedIconTheme: IconThemeData(color: Color(kAppColor)),
currentIndex: _selectedItemIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
setState(() {
_selectedItemIndex = index;
});
},
You should use a Pub/Sub mechanism. I prefer to use Rx in many situations and languages. For Dart/Flutter this is the package: https://pub.dev/packages/rxdart
For example, you can use a BehaviorSubject to emit data from widget A , pass the stream to widget B which listens for changes and applies them inside the setState .
Widget A:
// initialize subject and put it into the Widget B
BehaviorSubject<LiveOutput> subject = BehaviorSubject();
late WidgetB widgetB = WidgetB(deviceOutput: subject);
// when you have to emit new data
subject.add(deviceOutput);
Widget B:
// add stream at class level
class WidgetB extends StatefulWidget {
final ValueStream<LiveOutput> deviceOutput;
const WidgetB({Key? key, required this.deviceOutput}) : super(key: key);
@override
State<WidgetB> createState() => _WidgetBState();
}
// listen for changes
@override
void initState() {
super.initState();
widget.deviceOutput.listen((event) {
print("new live output");
setState(() {
// do whatever you want
});
});
}
In my app, often instead of using stateful widgets, I use mainly ChangeNotifierProvider<T>
in main.dart, some model class
class FooModel extends ChangeNotifier {
var _foo = false;
void changeFooState() {
_foo = true;
notifyListeners();
}
bool getFoo () => _foo;
}
and
var foo = context.read<FooModel>();
# or
var foo = context.watch<FooModel>();
in my stateless widgets. IMO this gives me more precise control over the rebuilding upon runtime state change, compared to stateful widgets.
The recipe can be found in the official docs , the concept is called "lifting state up".
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.