可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My Flutter Project structure is like this
Main() //Run App with MaterialApp and Routes
L HomePage() //Default route (/), with BottomNavigation
L MoviesPage() //Default BottomNavigation Index and shows a list of movies form TMDB
L DetailsPage()
L SeriesPage()
L SupportPage()
After clicking on any movie it navigates forward to the DetailsPage() but when I call Navigator.pop from DetailsPage() it should go back to the previous screen but it doesn't.
The Navigator.canPop(context) return false But the hardware back button works absolutely fine, so how do I fix it?
main.dart
class BerryMain extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Inferno(
{
'/': (context, argumets) => HomePage(),
'/detailspage': (context, arguments) => DetailsPage(arguments),
},
).home(context),
);
}
}
homepage
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _childnav = [MoviesPage(), SeriesPage(), SupportPage()];
void onTabPressed(...)
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('...'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
)
],
),
body: _childnav[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabPressed,
currentIndex: _currentIndex, //this property defines current active tab
items: [
BottomNavigationBarItem(
icon: Icon(Icons.movie), title: Text('Movies')),
BottomNavigationBarItem(icon: Icon(Icons.tv), title: Text('Series')),
BottomNavigationBarItem(icon: Icon(Icons.help), title: Text('Help'))
],
),
);
}
}
MoviesPage
//Inside ListView Builder
Virgil.pushNamed(context, '/detailspage', arguments: args);
DetailsPage
//Inside MaterialApp > Scaffold > SliverAppbar > BackButton
Navigator.pop(context)
I'm using Virgil but I don't think it is the problem.
回答1:
This can happen if your MoviesPage
has another MaterialApp
widget. Most of the time you want to use Scaffold
as a top widget for a new page/screen, but if you accidentally use MaterialApp
instead, nothing warns you.
What happens, is that MaterialApp
creates a new Navigator
, so if you switch from one page with MaterialApp
to another, you now have two Navigators in the widget tree.
The call Navigator.of(context)
looks for the closest Navigator, so it'll use the one, newly created in your MoviesPage
. As the history of your route transitions is stored in a first Navigator, this one can't pop back – it has empty route history.
Hence, the black screen.
Long story short, to fix this, just use Scaffold
as a top widget instead of MaterialApp
in all nested screens.
回答2:
I resolve it using this code:
Navigator.pushReplacementNamed(context, '/route')
回答3:
I had a very similar problem and my solution was to pass BuildContext context from the created StatelessWidget to a global variable and pop that context from the global variable. So...
var globalContext; // Declare global variable to store context from StatelessWidget
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
globalContext = context; // globalContext receives context from StetelessWidget.
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
canvasColor: Colors.transparent,
primarySwatch: Colors.blue,
),
home: SecondScreenPage(title: 'SECOND SCREEN'),
);
}
}
class SecondScreenPage extends StatefulWidget {
SecondScreenPage({Key key, this.title}) : super(key: key);
final String title;
@override
_SecondScreenPageState createState() => _SecondScreenPageState();
}
class _SecondScreenPageState extends State<SecondScreenPage>() {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(globalContext), // POPPING globalContext
)
),
...
}
Now I don't get a black screen and I'm at my first page as if I had clicked on Android's back button. =)
I don't know if that is a good practice. If anyone knows a way to do that "more correctly", please feel free to answer.
回答4:
According to this Answer I figured it out a way.
You should pass the MoviesPage
context to the DetailsPage
. And that context should be called in Navigator.pop(context);
for Example:
FIRST SCREEN
class FirstScreen extends StatelessWidget {
final BuildContext context;
FirstScreen(this.context);///here u have to call the build Context of FirstScreen
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: FirstScreenScreen(this.context));
}
}
class FirstScreenScreen extends StatefulWidget {
final BuildContext context;
FirstScreenScreen(this.context);/// and here also.
@override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreenScreen> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
child: new RaisedButton(onPressed: (){
navigateAndDisplaySelection(context);
},
child: new Text('Go to SecondPage'),
),
),
);
}
}
SECOND SCREEN
class SecondScreen extends StatelessWidget {
final BuildContext context;
SecondScreen(this.context);///here u have to call the build Context of FirstScreen
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: SecondScreenScreen(this.context));
}
}
class SecondScreenScreen extends StatefulWidget {
final BuildContext context;
SecondScreenScreen(this.context);/// and here also.
@override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreenScreen> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
child: new RaisedButton(onPressed: (){
Navigator.pop(widget.context);///this context is passed from firstScreen
},
child: new Text('Go Back'),
),
),
);
}
}
Pass Data & Return
if you want pass data and return, add a constructor with parameter in SecondScreen and
call setState in method Fisrtlike this:
navigateAndDisplaySelection(
BuildContext context) async {
// Navigator.push returns a Future that will complete after we call
// Navigator.pop on the Second Screen!
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => new SecondScreen(context)),
);
setState(() {
/// the passed value data will change here, when you change in second screen
});
}
回答5:
I had similar issues while poping back views, the way I fixed was instead of Navigator.of(context).pop();
I use Navigator.of(context).maybePop();
.
No more black screens after that.
回答6:
Flutter App should have only 1 MaterialApp
Why???
It is a core component that give access to all other elements
Technically thinking Material App contains App Title and a Home Screen widget
Here is my implementation
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'First Flutter App',
home: FirstActivity());
}
}
class FirstActivity extends StatefulWidget {
@override
_FirstActivityState createState() {
return _FirstActivityState();
}
}
class _FirstActivityState extends State<FirstActivity> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Activity'),
),
body: Center(
child: RaisedButton(
child: Text('Move Forward'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondActivity()),
);
}),
),
);
}
}
class SecondActivity extends StatefulWidget {
@override
_SecondActivityState createState() {
return _SecondActivityState();
}
}
class _SecondActivityState extends State<SecondActivity> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Activity'),
),
body: Center(
child: RaisedButton(
child: Text('Move Back'),
onPressed: () {
Navigator.pop(context);
}),
),
);
}
}
回答7:
Import page where you want to go.
Use Class of that Page-Ex.Appointment
Navigator.push(context,new MaterialPageRoute(builder: (context)=> new Appointment() ),);
3. Remove Push name from Navigator
回答8:
Workaround is SystemNavigator.pop();
refer from this link