Flutter Navigator.pop(上下文)返回黑屏

时间:2018-12-11 11:33:09

标签: dart flutter

我的Flutter Project的结构是这样的

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()

在单击任何电影后,它会向前导航到DetailsPage(),但是当我从DetailsPage()调用Navigator.pop时,它应该返回上一个屏幕,但是没有。

Navigator.canPop(context)返回false,但是硬件后退按钮工作正常,因此如何解决?

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),
    );
  }
}

主页

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'))
        ],
      ),
    );
  }
}

电影页面

//Inside ListView Builder
Virgil.pushNamed(context, '/detailspage', arguments: args);

DetailsPage

//Inside MaterialApp > Scaffold > SliverAppbar > BackButton
Navigator.pop(context)

我正在使用Virgil,但我不认为这是问题所在。

18 个答案:

答案 0 :(得分:9)

如果您的MoviesPage有另一个MaterialApp小部件,则可能会发生这种情况。大多数情况下,您想将Scaffold用作新页面/屏幕的顶部小部件,但是如果您不小心使用MaterialApp,则没有任何警告。

发生的事情是MaterialApp创建了一个新的Navigator,因此,如果您从带有MaterialApp的页面切换到另一页面,则现在在小部件树中有两个导航器。 >

呼叫Navigator.of(context)寻找最近的导航器,因此它将使用在您的MoviesPage中新创建的导航器。由于您的路线转换历史记录存储在第一个导航器中,因此该导航器不会弹出-它具有空的路线历史记录。

因此,黑屏。

长话短说,要解决此问题,只需在所有嵌套屏幕中使用Scaffold作为顶部小部件,而不要使用MaterialApp

答案 1 :(得分:6)

只需添加参数DECLARE @Username nvarchar(MAX) ,@Email nvarchar(MAX) ,@body nvarchar(MAX) ,@repQuery NVARCHAR(max) ,@filename NVARCHAR(40); DECLARE eAddresses CURSOR FOR SELECT UserName_ID, concat(UserName_ID,'@yahoo.com') FROM tbl_2Admin_UserGroups WHERE UserName_ID = 'johnsmith' OR UserName_ID = 'joebloggs'; OPEN eAddresses; FETCH NEXT FROM eAddresses INTO @Username, @email; WHILE @@FETCH_STATUS = 0 BEGIN PRINT @email; SET @repQuery = 'SELECT * FROM tbl_2Admin_UserGroups WHERE UserName_ID = @username' SET @filename = N'Depo_User_Details_' + convert(varchar, getdate(), 112) + '.csv' DECLARE @Delimiter Char(1) SET @Delimiter = CHAR(9) DECLARE @mailitem_id INT; EXEC msdb..sp_send_dbmail @recipients = @email, @subject = N'Depo User Details', @body = N'Please find attached', @query =@repQuery, @attach_query_result_as_file = 1, @query_attachment_filename = @filename, @query_result_header = 1, @query_result_width = 1000, @Query_Result_Separator = @Delimiter, @exclude_query_output = 1, @query_result_no_padding = 1, @mailitem_id = @mailitem_id OUTPUT, @reply_to = 'noreply@yahoo.com' FETCH NEXT FROM eAddresses INTO @Username, @Email; END; CLOSE eAddresses; DEALLOCATE eAddresses; : 样本:

rootNavigator: true

编辑:在某些情况下,此解决方案可能无法解决您的问题。请确保您的应用程序中所有页面都只有一个Navigator.of(context, rootNavigator: true).pop(context); 小部件。

答案 2 :(得分:2)

解决方法是SystemNavigator.pop(); refer from this link

答案 3 :(得分:1)

根据此Answer,我找到了解决方法。

您应该将MoviesPage上下文传递给DetailsPage。并且该上下文应在Navigator.pop(context);

中调用

例如: 第一屏

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'),
            ),
          ),
       );
     }
    }

第二个屏幕

 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'),
            ),
          ),
       );
     }
    }

传递数据并返回  
如果要传递数据并返回,请在SecondScreen中添加带有参数的构造函数,然后 像这样在方法Fisrt中调用setState:

 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
    });
  }

答案 4 :(得分:1)

我使用以下代码解决它:

Navigator.pushReplacementNamed(context, '/route')

答案 5 :(得分:0)

我想分享我在这方面的经验,因为我遇到了同样的问题。

基本上,来自空屏幕堆栈pop是导致此问题的主要原因。

就我而言,我在scanner_screen(通过我的main_screen 上的按钮推送到堆栈)上执行周期性操作(从相机读取一些数据),当相机找到特定文本时,我调用{{ 1}}。问题是相机读取速度太快,因此我pop多次重复,这意味着从空堆栈中弹出。

起初,我认为弹出足够快,可以在我的扫描仪屏幕上调用 dispose 以停止调用不断弹出的周期性操作,但事实并非如此。这就是为什么我需要确保在从屏幕堆栈弹出之前手动停止周期性操作。

我希望这可以帮助某人。

答案 6 :(得分:0)

我有同样的问题,比如返回同一个屏幕返回黑屏

    void main() async {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return GetMaterialApp(home: Todo());
      }
    }
    
    class Todo extends StatefulWidget {
      const Todo({Key key}) : super(key: key);
    
    
      @override
      _TodoState createState() => _TodoState();
    }
    
    class _TodoState extends State<Todo> {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
     body: RaisedButton(onPressed: (){
            //print(controler.text);
            //Navigator.push(context,MaterialPageRoute(builder: (context) => Todo("viral")));
            //_sendDataBack(context);
            Get.to(()=>Test());
    
          },child: Text("submit"),),
        ) ;
    
      }
    }
    
    other class
    
    class Test extends StatefulWidget {
      const Test({Key key}) : super(key: key);
    
      @override
      _TestState createState() => _TestState();
    }
    
    class _TestState extends State<Test> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: RaisedButton(onPressed: (){
            Get.to(()=>MyApp());
    
          },child: Text("submit"),),
        ) ;
    
      }
    }

然后我检查我的代码并意识到我传递了错误的类我必须返回具有我的小部件树的类,因此将 Myapp() 替换为 Todo() 如下所示并解决我的问题

class Test extends StatefulWidget {
  const Test({Key key}) : super(key: key);

  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RaisedButton(onPressed: (){
        Get.to(()=>MyApp());

      },child: Text("submit"),),
    ) ;

  }
}

答案 7 :(得分:0)

这对我有用,我认为这是更好的解决方案。

WidgetsBinding.instance.addPostFrameCallback((_) {
            Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => Menu()));
          }),

答案 8 :(得分:0)

我也遇到了这个问题,尝试了所有先前的答案后我仍然黑屏。我对MaterialApp或Scafold原因一无所知,因为我的应用程序main.dart仅使用MaterialApp,所以Scafold解决方案没有为我工作。现在为我解决了什么。

弹出式密码

Navigator.of(context, rootNavigator: true).pop(context)

没有正常工作的代码

 Navigator.pushReplacement(context,CupertinoPageRoute(fullscreenDialog: false,builder: (context) => ChildClass())),

有效

的代码
 Navigator.push(context,MaterialPageRoute(builder: (context) => ChildClass())

答案 9 :(得分:0)

在导航到另一个屏幕之前,请使用Navigator.pop(context)

示例:

Navigator.pop(context),
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(uilder: (_) => GoogleMapScreenRemasterd()),
(Route<dynamic> route) => false),

答案 10 :(得分:0)

要摆脱黑屏,您还可以将以下方法与所需的pop()一起使用

Navigator.of(context)..pop()..pop();

答案 11 :(得分:0)

也许在详细屏幕中,您将MaterialApp用作主要窗口小部件,您只需要删除MaterialApp并直接返回Scaffold,也许您的问题就可以解决此问题,对我来说就可以了。

Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
       body: Container(),
     ),
    );
  }

代替

Widget build(BuildContext context) {
 return Scaffold(
   body: Container(),
 );
}

答案 12 :(得分:0)

删除initialRoute时也会出现此行为,如果您使用以下方法导航到第一个孩子,则可能会发生这种情况:

Navigator.popAndPushNamed('...')

解决方案显然很简单,只需使用pushNamed

答案 13 :(得分:0)

  1. 将页面导入到想要的页面。

  2. 使用该Page-Ex.Appointment

  3. 的类
Navigator.push(context,new MaterialPageRoute(builder: (context)=> new Appointment() ),);

3. Remove Push name from Navigator

答案 14 :(得分:0)

在弹出视图时,我遇到了类似的问题,我修复的方式不是使用Navigator.of(context).pop();的{​​{1}}。

此后不再有黑屏。

答案 15 :(得分:0)

我有一个非常相似的问题,我的解决方案是将BuildContext context 从创建的StatelessWidget传递到全局变量,然后从全局变量中弹出该上下文。所以...

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
        )
      ),
  ...
}

现在,我没有黑屏,而且我在首页上好像单击了Android的后退按钮一样。 =)

我不知道这是否是一个好习惯。如果有人知道“更正确”的方法,请随时回答。

答案 16 :(得分:-1)

可能导致黑屏的另一个原因是,可能正在使用

Navigator.of(context).pushReplacementNamed('yourRoute') 而不是使用普通的 Navigator.of(context).pushNamed('yourRoute')Navigator.of(context).push(newRoute) 路由到新屏幕。

由于 Navigator.pushReplacementNamed(context, 'yourRoute') 处理堆栈中最后/最顶部的屏幕,因此当您使用 Navigator.pop(context) 时,它会将您带到一个已处理的空屏幕,因此是黑屏。

答案 17 :(得分:-1)

这是一个常见的问题。这些只是简单的问题。当您按下 New screen 并且还有 MaterialApp() 小部件时会发生这种情况。