Saya punya satu StatefulWidget
di Flutter dengan tombol, yang menavigasi saya ke yang lain StatefulWidget
menggunakan Navigator.push()
. Pada widget kedua saya mengubah status global (beberapa preferensi pengguna). Ketika saya kembali dari widget kedua ke widget pertama, menggunakan Navigator.pop()
widget pertama dalam keadaan lama, tetapi saya ingin memaksanya memuat ulang. Ada ide bagaimana caranya mengerjakan ini? Saya punya satu ide tapi kelihatannya jelek:
- pop untuk menghapus widget kedua (yang sekarang)
- pop lagi untuk menghapus widget pertama (yang sebelumnya)
- dorong widget pertama (itu harus memaksa menggambar ulang)
Jawaban:
Ada beberapa hal yang dapat Anda lakukan di sini. Jawaban @ Mahi sementara benar bisa jadi sedikit lebih ringkas dan benar-benar menggunakan push daripada showDialog seperti yang ditanyakan OP. Ini adalah contoh yang menggunakan
Navigator.push
:import 'package:flutter/material.dart'; class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( color: Colors.green, child: new Column( children: <Widget>[ new RaisedButton( onPressed: () => Navigator.pop(context), child: new Text("back"), ), ], ), ); } } class FirstPage extends StatefulWidget { @override State<StatefulWidget> createState() => new FirstPageState(); } class FirstPageState extends State<FirstPage> { Color color = Colors.white; @override Widget build(BuildContext context) { return new Container( color: color, child: new Column( children: <Widget>[ new RaisedButton( child: new Text("next"), onPressed: () { Navigator .push( context, new MaterialPageRoute(builder: (context) => new SecondPage()), ) .then((value) { setState(() { color = color == Colors.white ? Colors.grey : Colors.white; }); }); }), ], ), ); } } void main() => runApp( new MaterialApp( builder: (context, child) => new SafeArea(child: child), home: new FirstPage(), ), );
Namun, ada cara lain untuk melakukan ini yang mungkin sesuai dengan kasus penggunaan Anda. Jika Anda menggunakan
global
sebagai sesuatu yang mempengaruhi pembuatan halaman pertama Anda, Anda dapat menggunakan InheritedWidget untuk menentukan preferensi pengguna global Anda, dan setiap kali mereka diubah FirstPage Anda akan dibangun kembali. Ini bahkan berfungsi dalam widget stateless seperti yang ditunjukkan di bawah ini (tetapi harus berfungsi dalam widget stateful juga).Contoh inheritedWidget dalam flutter adalah Tema aplikasi, meskipun mereka mendefinisikannya dalam widget alih-alih membuatnya langsung dibangun seperti yang saya miliki di sini.
import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( color: Colors.green, child: new Column( children: <Widget>[ new RaisedButton( onPressed: () { ColorDefinition.of(context).toggleColor(); Navigator.pop(context); }, child: new Text("back"), ), ], ), ); } } class ColorDefinition extends InheritedWidget { ColorDefinition({ Key key, @required Widget child, }): super(key: key, child: child); Color color = Colors.white; static ColorDefinition of(BuildContext context) { return context.inheritFromWidgetOfExactType(ColorDefinition); } void toggleColor() { color = color == Colors.white ? Colors.grey : Colors.white; print("color set to $color"); } @override bool updateShouldNotify(ColorDefinition oldWidget) => color != oldWidget.color; } class FirstPage extends StatelessWidget { @override Widget build(BuildContext context) { var color = ColorDefinition.of(context).color; return new Container( color: color, child: new Column( children: <Widget>[ new RaisedButton( child: new Text("next"), onPressed: () { Navigator.push( context, new MaterialPageRoute(builder: (context) => new SecondPage()), ); }), ], ), ); } } void main() => runApp( new MaterialApp( builder: (context, child) => new SafeArea( child: new ColorDefinition(child: child), ), home: new FirstPage(), ), );
Jika Anda menggunakan widget yang diwariskan, Anda tidak perlu khawatir tentang melihat sembulan halaman yang Anda dorong, yang akan berfungsi untuk kasus penggunaan dasar tetapi mungkin berakhir dengan masalah dalam skenario yang lebih kompleks.
sumber
Ada 2 hal, meneruskan data dari
Halaman ke-1 sampai ke-2
Gunakan ini di halaman pertama
// sending "Foo" from 1st Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
Gunakan ini di halaman ke-2.
class Page2 extends StatelessWidget { final String string; Page2(this.string); // receiving "Foo" in 2nd ... }
Halaman ke-2 sampai ke-1
Gunakan ini di halaman ke-2
// sending "Bar" from 2nd Navigator.pop(context, "Bar");
Gunakan ini di halaman pertama, ini sama dengan yang digunakan sebelumnya tetapi dengan sedikit modifikasi.
// receiving "Bar" in 1st String received = await Navigator.push(context, MaterialPageRoute(builder: (_) => Page2("Foo")));
sumber
Anda dapat menggunakan pushReplacement dan menentukan Route baru
sumber
The Trik Mudah adalah dengan menggunakan Navigator.pushReplacement metode
Halaman 1
Halaman 2
sumber
Ini bekerja dengan sangat baik, saya dapatkan dari dokumen ini dari halaman flutter : flutter doc
Saya mendefinisikan metode untuk mengontrol navigasi dari halaman pertama.
_navigateAndDisplaySelection(BuildContext context) async { final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => AddDirectionPage()), ); //below you can get your result and update the view with setState //changing the value if you want, i just wanted know if i have to //update, and if is true, reload state if (result) { setState(() {}); } }
Jadi, saya menyebutnya dalam metode tindakan dari wadah tinta, tetapi dapat dipanggil juga dari tombol:
Dan akhirnya di halaman kedua, untuk mengembalikan sesuatu (saya mengembalikan bool, Anda dapat mengembalikan apa pun yang Anda inginkan):
onTap: () { Navigator.pop(context, true); }
sumber
await Navigator....
menghilangkan nilai hasil dalam pop.Letakkan ini di tempat Anda mendorong ke layar kedua (di dalam fungsi async)
Function f; f= await Navigator.pushNamed(context, 'ScreenName'); f();
Taruh ini di tempat Anda muncul
Itu
setState
dipanggil di dalampop
closure untuk memperbarui data.sumber
setState
sebagai argumen? Anda pada dasarnya meneleponsetState
di dalam closure. Tidak menyampaikannya sebagai argumen.Navigator.pop
.solusi saya pergi dengan menambahkan parameter fungsi pada SecondPage, kemudian menerima fungsi reload yang dilakukan dari FirstPage, kemudian menjalankan fungsi sebelum baris Navigator.pop (konteks).
Halaman pertama
refresh() { setState(() { //all the reload processes }); }
lalu mendorong ke halaman berikutnya ...
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage(refresh)),);
SecondPage
final Function refresh; SecondPage(this.refresh); //constructor
lalu di depan garis pop navigator,
widget.refresh(); // just refresh() if its statelesswidget Navigator.pop(context);
Segala sesuatu yang perlu dimuat ulang dari halaman sebelumnya harus diperbarui setelah pop.
sumber
Anda dapat mengirimkan kembali
dynamic result
ketika Anda memunculkan konteks dan kemudian memanggilsetState((){})
ketika nilainyatrue
dinyatakan biarkan saja keadaan apa adanya.Saya telah menempelkan beberapa potongan kode untuk referensi Anda.
handleClear() async { try { var delete = await deleteLoanWarning( context, 'Clear Notifications?', 'Are you sure you want to clear notifications. This action cannot be undone', ); if (delete.toString() == 'true') { //call setState here to rebuild your state. } } catch (error) { print('error clearing notifications' + error.toString()); } } Future<bool> deleteLoanWarning(BuildContext context, String title, String msg) async { return await showDialog<bool>( context: context, child: new AlertDialog( title: new Text( title, style: new TextStyle(fontWeight: fontWeight, color: CustomColors.continueButton), textAlign: TextAlign.center, ), content: new Text( msg, textAlign: TextAlign.justify, ), actions: <Widget>[ new Container( decoration: boxDecoration(), child: new MaterialButton( child: new Text('NO',), onPressed: () { Navigator.of(context).pop(false); }, ), ), new Container( decoration: boxDecoration(), child: new MaterialButton( child: new Text('YES', ), onPressed: () { Navigator.of(context).pop(true); }, ), ), ], ), ) ?? false; }
Salam, Mahi
sumber
Navigator.pop(context, true)
, bukan? Tetapi bagaimana saya bisa mendapatkantrue
nilai ini ? MenggunakanBuildContext
?setState() called after dispose(): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. ....
if(!mounted) return;
sebelum setiap panggilan dengansetState((){});
cara ini kamu bisa menghindari update widget yang dibuang atau widget yang sudah tidak aktif lagi.Diperlukan untuk memaksa membangun kembali salah satu widget tanpa kewarganegaraan saya. Tidak ingin menggunakan stateful. Datang dengan solusi ini:
await Navigator.of(context).pushNamed(...); ModalRoute.of(enclosingWidgetContext);
Perhatikan bahwa konteks dan enklosurWidgetContext bisa jadi konteks yang sama atau berbeda. Jika, misalnya, Anda mendorong dari dalam StreamBuilder, hasilnya akan berbeda.
Kami tidak melakukan apa pun di sini dengan ModalRoute. Tindakan berlangganan saja sudah cukup untuk memaksa pembangunan kembali.
sumber
Jika Anda menggunakan dialog peringatan, Anda dapat menggunakan Future yang selesai saat dialog ditutup. Setelah selesai masa depan Anda dapat memaksa widget untuk memuat ulang negara.
Halaman pertama
onPressed: () async { await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( .... ); } ); setState(() {}); }
Dalam dialog Peringatan
sumber
Hari ini saya menghadapi situasi yang sama tetapi saya berhasil menyelesaikannya dengan cara yang jauh lebih mudah, saya hanya mendefinisikan variabel global yang digunakan di kelas stateful pertama, dan ketika saya menavigasi ke widget stateful kedua saya membiarkannya memperbarui nilai global variabel yang secara otomatis memaksa widget pertama untuk diperbarui. Berikut ini contohnya (Saya menulisnya dengan tergesa-gesa jadi saya tidak memasang perancah atau aplikasi material, saya hanya ingin mengilustrasikan maksud saya):
import 'package:flutter/material.dart'; int count = 0 ; class FirstPage extends StatefulWidget { FirstPage({Key key}) : super(key: key); @override _FirstPageState createState() => _FirstPageState(); } class _FirstPageState extends State<FirstPage> { @override Widget build(BuildContext context) { return InkWell( onTap(){ Navigator.of(context).push(MaterialPageRoute(builder: (context) => new SecondPage()); }, child: Text('First', style : TextStyle(fontSize: count == 0 ? 20.0 : 12.0)), ), } class SecondPage extends StatefulWidget { SecondPage({Key key}) : super(key: key); @override _SecondPageState createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { @override Widget build(BuildContext context) { return IconButton( icon: new Icon(Icons.add), color: Colors.amber, iconSize: 15.0, onPressed: (){ count++ ; }, ), }
sumber
Navigator.pop()
itu akan mempertahankan keadaan sebelumNavigator.push()
sampaisetState
dipanggil lagi, apa yang tidak terjadi dalam kode ini. Apakah saya melewatkan sesuatu?Kode sederhana ini bekerja bagi saya untuk pergi ke root dan memuat ulang status:
... onPressed: () { Navigator.of(context).pushNamedAndRemoveUntil('/', ModalRoute.withName('/')); }, ...
sumber