在Flutter中导航到新屏幕

50

4
我不确定我对此的感觉如何。我认为问题的水平是有限度的。我的意思是,如果这个问题/答案可以接受,那么我们就可以针对框架中的任何小部件进行类似提问。 - Rémi Rousselet
3
可以,但是我认为你的问题属于“范围过广”。 - Rémi Rousselet
我认为问题的层次必须有一个限制。正如@RémiRousselet所说,我们可以针对框架中的任何小部件进行操作,而且这个问题的文档也非常明显。 - westdabestdb
Flutter文档在这个主题上非常直接明了。https://flutter.dev/docs/cookbook/navigation/navigation-basics - user482594
13个回答

70

导航至新屏幕:

Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));

其中 context 是小部件的 BuildContext,NewScreen 是第二个小部件布局的名称。

图片描述

代码

main.dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home Screen')),
      body: Center(
        child: ElevatedButton(
          child: const Text(
            'Navigate to a new screen >>',
            style: TextStyle(fontSize: 24.0),
          ),
          onPressed: () {
            _navigateToNextScreen(context);
          },
        ),
      ),
    );
  }

  void _navigateToNextScreen(BuildContext context) {
    Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
  }
}

class NewScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('New Screen')),
      body: const Center(
        child: Text(
          'This is a new screen',
          style: TextStyle(fontSize: 24.0),
        ),
      ),
    );
  }
}

另请参阅


3
为什么你每隔一天就这样做?这是什么原因?也许你应该写一个博客之类的东西吗? - pskink
1
@westdabestdb他每周都这样做3-4次,我不知道这样做的目的是什么... - pskink
3
“我们都知道Flutter有很好的文档。” - 我觉得这一点值得商榷... ;-( - pskink
2
@pskink 确实,阅读源代码比花时间阅读文档要好10倍,所以两者兼顾才是解决问题的方法。 - westdabestdb
3
我在 SO 上回答问题是因为它帮助我更好地学习。我回答的几乎每一个问题都是我自己曾经遇到过的问题。当我有问题时,我会先用谷歌搜索。如果我找不到快速答案或 SO 上没有类似问题,那么我就会编写一个问答对。 - Suragch
显示剩余4条评论

23

使用Flutter预先制作的动画,可以使用它们各自的转换类来加载新屏幕。例如:

Container Transformation

enter image description here

基本上我们需要将第一个小部件或屏幕变形为下一个屏幕。为此,我们需要使用OpenContainer。以下代码演示了ListView中的一个项目转换为其详细页。

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.white,
      elevation: 2.0,
      child: OpenContainer(
        transitionType: ContainerTransitionType.fadeThrough,
        closedColor: Theme.of(context).cardColor,
        closedElevation: 0.0,
        openElevation: 4.0,
        transitionDuration: Duration(milliseconds: 1500),
        openBuilder: (BuildContext context, VoidCallback _) => THENEXTSCREEN(),
        closedBuilder: (BuildContext _, VoidCallback openContainer) {
          return ListTile(
            leading: Icon(Icons.album),
            title: Text("ITEM NAME"),
          );
        },
      ),
    );
  }

共享轴

enter image description here

这个动效类似于标签页或步骤条,我们需要使用 SharedAxisTransitionPageTransitionSwitcher 和一个状态来表示当前活动页面和上一个页面之间的转换。如果只是在两个页面之间切换,我们可以用一个简单的布尔值 isFirstPage 来实现。下面是使用 Provider 作为状态管理的代码片段:

  @override
  Widget build(BuildContext context) {
    return Consumer<YourState>(
      builder: (context, state, child) {
        return PageTransitionSwitcher(
          duration: const Duration(milliseconds: 1500),
          reverse: !state.isFirstPage, // STATE
          transitionBuilder: (
            Widget child,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
          ) {
            return SharedAxisTransition(
              child: child,
              animation: animation,
              secondaryAnimation: secondaryAnimation,
              transitionType: SharedAxisTransitionType.horizontal,
            );
          },
          child: state.isFirstPage? FIRSTPAGE() : SECONDPAGE(), // STATE
        );
      },
    );
  }

请注意,在所有这些场景中,我们不使用Navigator和MaterialPageRoute。所有这些代码都是从动画库中派生出来的,因此您可能希望先检查它。


14

使用Navigator.push()通过后退导航到下一个屏幕

Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),);

使用Navigator.pushReplacement()跳转到下一个屏幕而不返回上一个屏幕。

Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => SecondRoute()),);

5

这是一个完整的路由推送/弹出的示例:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Routes',
      routes: {
        '/login': (BuildContext context) => Login(),
        // add another route here
        // '/register': (BuildContext context) => Register(),
      },
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Routes'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            RaisedButton(
              onPressed: () {
                // This gives the back button:
                Navigator.of(context).pushNamed('/login');

                // This doesn't give the back button (it replaces)
                //Navigator.pushReplacementNamed(context, '/login');
              },
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}

class Login extends StatefulWidget {
  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Login Page'),
        ),
        body: Center(
          child: RaisedButton(
            onPressed: () {
              // This will only work for pushNamed
              Navigator.of(context).pop();
            },
            child: Text('Go back'),
          ),
        ));
  }
}

5
onTap: () {
  Navigator.push(context,
      MaterialPageRoute(builder: (context) => NextScreenName()));
}

虽然你的答案可能是正确的(我不知道是否正确),但如果你解释一下它是做什么的以及为什么有效,对于未来的搜索者来说会更有价值。 - MindSwipe
嗨@MindSwipe,请参考问题。 基本上,它用于从一个屏幕导航到另一个屏幕。 - Chandsi Gupta

4
如果你熟悉Web开发,这种方法类似于路由。
main.dart
void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      routes: {
        '/' : (BuildContext context)=>HomePage(),
        '/register' : (BuildContext context)=>RegisterPage(),
      },
    );
  }
}

您可以按照以下步骤从homepage.dart页面向register.dart页面添加按钮 onPressed 事件。

onPressed: (){
    Navigator.pushReplacementNamed(context, '/register');
 },

4

在形式化方法中:

Navigator.push(context, MaterialPageRoute(builder: (context)=>Second()));

在 GetX 方法中:

Get.to(Second());

如果我们可以将屏幕导航到另一个页面并从堆栈中删除当前页面,那么我们可以使用以下定义的方法:
Get.off(Third());

如果我们可以将屏幕导航到另一个页面并从堆栈中删除所有路由或页面,那么我们可以使用以下定义的方法:
Get.offAll(Third());

如果我们想要使用 Navigator.pop(),那么 GetX 提供了一个下面定义的方法:
Get.back();

3
FloatingActionButton(
  onPressed: (){
    Navigator.of(context).push(MaterialPageRoute(builder: (context) => const AddUser()));
  },
  child: const Icon(Icons.add),
),

2
欢迎来到Stack Overflow。代码配合解释会更有帮助。Stack Overflow的宗旨是学习,而不是提供盲目复制粘贴的片段。当回答旧问题(这个问题已经快4年了)并且已经有现有答案时,这一点尤为重要。请编辑您的答案,解释它如何回答特定的问题,并改进已有的内容。请参见[答案]。 - Chris

2
您可以在构建小部件时使用这种方式:

onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => NewScreen()));},

请注意保留HTML标签。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接