Flutter Navigator 2.0


ソース:https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade

Flutter Navigator 1.0


flut開発者であれば、次のレベルに慣れることができます.
  • Navigator-Routeオブジェクトスタック管理部
  • Route-Navigatorで管理されているオブジェクトペイント画面を使用します.通常はMaterialPageRouteのようなクラスとして実装される.
  • Navigator 1.0は、通常、Navigatorが命名された匿名のルーティングをRouteスタックにプッシュしてポップアップする形式である.

    Anonymous routes


    ルーティング定義がない場合は、ウィジェットを呼び出すように画面を移動します.
    //화면 이동
    Navigator.push(
    	context,
        MaterialPageRoute(builder: (context) {
        	return NextPage();
        }),
    );
    
    //화면 나가기
    Navigator.pop(context);

    Named routes


    MaterialAppまたはCupertinAppでルーティングを事前定義し、定義したルーティング名で画面を移動します.
    class App extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: {
          	//여기서 route name에 따라 이동할 화면을 미리 정의.
            '/': (context) => HomeScreen(),
            '/details': (context) => DetailScreen(),
          },
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('View Details'),
              onPressed: () {
              	//화면 이동시 route name을 통해 호출
                Navigator.pushNamed(
                  context,
                  '/details',
                );
              },
            ),
          ),
        );
      }
    }
    

    Named routions深化プロセス

    onGenerateRoute: (settings) {
      //route name을 파싱해서 parameter에 따라 동적으로 화면을 호출.
      //web의 path처럼 사용 가능.
      var uri = Uri.parse(settings.name);
      if (uri.pathSegments.length == 2 &&
          uri.pathSegments.first == 'details') {
        var id = uri.pathSegments[1];
        return MaterialPageRoute(builder: (context) => DetailScreen(id: id));
      }
      
      return MaterialPageRoute(builder: (context) => UnknownScreen());
    },

    Flutter Navigator 2.0


    Navigator 2.0では、アプリケーションの画面がアプリケーションの状態で動作し、基本プラットフォーム上でルーティングパケットを行う機能を提供するクラスがいくつか追加されています.

    Navigator 2.0のインタラクティブグラフィックス

    Page


    Navigatorヒストリスタックを構成する不変オブジェクト(可変オブジェクト)
    
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Books App',
          //navigator 안에다 page들을 넣는다.
          home: Navigator(
            pages: [
            //이렇게 MaterialPage를 사용해도 되고
              MaterialPage(
                child: BooksListScreen(),
              ),
            ],
          ),
        );
      }
      
     //이런 식으로 Page를 상속 받아서 구현해도 된다.
     class BookDetailsPage extends Page {
      final Book book;
      
      BookDetailsPage({
        this.book,
      }) : super(key: ValueKey(book));
      
      Route createRoute(BuildContext context) {
        return PageRouteBuilder(
          settings: this,
          pageBuilder: (context, animation, animation2) {
            final tween = Tween(begin: Offset(0.0, 1.0), end: Offset.zero);
            final curveTween = CurveTween(curve: Curves.easeInOut);
            return SlideTransition(
              position: animation.drive(curveTween).drive(tween),
              child: BookDetailsScreen(
                key: ValueKey(book),
                book: book,
              ),
            );
          },
        );
      }
    }
     

    Router


    Navigatorが表示するページを設定します.一般的に、ページリストは、基本プラットフォーム(android、ios、web)またはアプリケーションのステータス変更によって引き起こされます.
     return MaterialApp.router(
          title: 'Books App',
          routeInformationParser: _routeInformationParser,
          routerDelegate: _routerDelegate,
        );

    RouteInformationParser


    RouteInformationProviderからRouteInformationを受信し、ユーザー定義のデータ型に分類します.
    //RouteInformationParser를 상속 받아서 구현
    //BookRoutePath가 user-defined data type
    class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> {
    
    //RouteInformation을 파싱해서 user-defined data type으로 리턴
      
      Future<BookRoutePath> parseRouteInformation(
          RouteInformation routeInformation) async {
        final uri = Uri.parse(routeInformation.location);
    
        // Handle '/book/:id'
        if (uri.pathSegments.length == 2) {
          if (uri.pathSegments[0] != 'book') return BookRoutePath.unknown();
          var remaining = uri.pathSegments[1];
          var id = int.tryParse(remaining);
          if (id == null) return BookRoutePath.unknown();
          return BookRoutePath.details(id);
        }
      }
    
    //user-defined data type이 각각 어떤 Route와 대응되는지를 정의
      
      RouteInformation restoreRouteInformation(BookRoutePath path) {
        if (path.isUnknown) {
          return RouteInformation(location: '/404');
        }
        if (path.isHomePage) {
          return RouteInformation(location: '/');
        }
        if (path.isDetailsPage) {
          return RouteInformation(location: '/book/${path.id}');
        }
        return null;
      }
    }

    RouterDelegate


    Routerがappの状態変更をどのように処理して応答を返すかを定義します.
    class BookRouterDelegate extends RouterDelegate<BookRoutePath>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> {
      final GlobalKey<NavigatorState> navigatorKey;
    
    //...
    
      BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
    
      BookRoutePath get currentConfiguration {
        return _selectedBook == null
            ? BookRoutePath.home()
            : BookRoutePath.details(books.indexOf(_selectedBook));
      }
    
      
      Widget build(BuildContext context) {
        return Navigator(
          key: navigatorKey,
          pages: [
            MaterialPage(
              key: ValueKey('BooksListPage'),
              child: BooksListScreen(
                books: books,
                onTapped: _handleBookTapped,
              ),
            ),
    	if (_selectedBook != null)
              BookDetailsPage(book: _selectedBook)
          ],
          //Navigator.pop()이 호출 되었을 때 실행되는 부분.
          onPopPage: (route, result) {
            if (!route.didPop(result)) {
              return false;
            }
    
            // Update the list of pages by setting _selectedBook to null
            _selectedBook = null;
            show404 = false;
            notifyListeners();
    
            return true;
          },
        );
      }
    
    //user-defined data type을 전달받아 적절하게 state를 변경
    //예제에서는 _selectedBook을 업데이트
      
      Future<void> setNewRoutePath(BookRoutePath path) async {
    
        if (path.isDetailsPage) {
          if (path.id < 0 || path.id > books.length - 1) {
            show404 = true;
            return;
          }
    
          _selectedBook = books[path.id];
        } else {
          _selectedBook = null;
        }
    
        show404 = false;
      }
    
      void _handleBookTapped(Book book) {
        _selectedBook = book;
        notifyListeners();
      }
    }

    BackButtonDispatcher


    backbutton actionをRouterに渡します.

    の最後の部分


    プラットフォーム側で発生した予期せぬスクリーン変更(backbutton(android,web)、URL変更(web)に応答するための機能、より高度なページ制御サポート(multipopまたはpush、スタック内の特定のページの削除など)が追加されました.作業中にマルチpopがサポートされていないか、現在の画面の前の履歴のみが削除されているのは嬉しいニュースです.しかし,概念を全面的に変える更新であるため,移行は容易ではない.