go_router

go_router ์šฉ๋„(=๋ผ์šฐํŒ… ํŒจํ‚ค์ง€ ์‚ฌ์šฉ ๋ชฉ์ )

์‚ฌ์‹ค ๋”ฐ๋กœ ์ด๋ ‡๊ฒŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ Navigator ๋งŒ์œผ๋กœ ํ™”๋ฉด ๋ผ์šฐํŒ…์€ ๊ฐ€๋Šฅํ•˜๋‹ค. ํ•˜์ง€๋งŒ url based ๋œ ์ด๋™ ๋ฐฉ์‹, ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ , Named routes ๋“ฑ ๋งŽ์€ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์žˆ์–ด์„œ(=์‚ฌ์‹ค ๊ทธ๋งŒํผ ์ˆœ์ •์ด ์ œ๋Œ€๋กœ ์—ญํ• ์„ ๋ชปํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ด์•ผ๊ธฐ๊ฐ€ ๋œ๋‹ค) ๋”ฐ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

์™œ go_router ์‚ฌ์šฉํ•˜๋Š”๊ฐ€

Flutter Favorite ์ด๊ณ  ์•„์ฃผ ๋ณดํŽธ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. ๋ผ์šฐํ„ฐ ์ด๋™ ๊ด€๋ จํ•ด์„œ ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์ง€๋งŒ ์žฅ๋‹จ์„ ๋”ฐ์งˆ ๊ฒƒ ์—†์ด ์ œ์ผ ๋ณดํŽธ์ ์ด์–ด์„œ ์ผ๋‹จ ์ฑ„ํƒํ–ˆ๋‹ค.

go_router ๊ฐ„๋žตํ•œ ์†Œ๊ฐœ

์–ด์ฐจํ”ผ ์•„๋ž˜์— ์ƒ˜ํ”Œ ์ฝ”๋“œ๋ฅผ ์“ฐ๊ธด ํ• ๊ฑด๋ฐ ๋ณดํ†ต ์ƒˆ๋กœ์šด ์œ„์ ฏ์„ ์“ธ ๋•Œ ๋‚˜๋Š” ํ”Œ๋Ÿฌํ„ฐํŒ€์—์„œ ๋งŒ๋“  ์งง์€ ์œ„์ ฏ ์†Œ๊ฐœ ์˜์ƒ์„ ๋จผ์ € ๋ณธ๋‹ค. ์ˆฒ์„ ๋จผ์ € ๋ณด๊ณ  ๋‚˜๋ฌด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์–ด์„œ ์ข‹๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์œ„์ ฏ ์†Œ๊ฐœ ์˜์ƒ๋“ค์ด ๊น”๋”ํ•˜๊ฒŒ ์‹œ๊ฐํ™” ๋˜์–ด ์žˆ์–ด์„œ ์–ด๋–ค ์šฉ๋„์ธ์ง€, ๊ฐ„๋žตํ•˜๊ฒŒ๋‚˜๋งˆ ์–ด๋–ค์‹์œผ๋กœ ์“ฐ๋Š”์ง€ ๋“ฑ์„ ๋น ๋ฅธ ์‹œ๊ฐ„ ๋‚ด์— ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.

go_router ๊ตฌํ˜„

flutter pub add go_router
class RouterLocation {
  static const String splash = '/';
  static const String signIn = 'singIn';
}
import 'package:go_router/go_router.dart';

import '../../barrel.dart';
import '../environments/environment.dart';

class RouterConfiguration {
  static final GoRoute signIn = GoRoute(
    path: RouterLocation.signIn,
    name: RouterLocation.signIn,
    builder: (context, state) {
      return const SignInScreen();
    },
  );

  static final GoRouter router = GoRouter(
    initialLocation: RouterLocation.splash,
    debugLogDiagnostics: Environment.isProduction ? false : true,
    routes: [
      GoRoute(
        path: RouterLocation.splash,
        name: RouterLocation.splash,
        builder: (context, state) {
          return const SplashScreen();
        },
        routes: [
          signIn,
        ],
      ),
    ],
  );
}

๊ทธ๋ฆฌ๊ณ  environment ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ์œ„์ ฏ์— ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•œ๋‹ค.

  run() async {
    await Injector.registerDependencies();
    runApp(const ProjectName());
  }
import 'package:flutter/material.dart';

import '../../barrel.dart';

class ProjectName extends StatefulWidget {
  const ProjectName({super.key});

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

class _ProjectNameState extends State<ProjectName> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      routerConfig: RouterConfiguration.router,
    );
  }
}

go, push ์ฐจ์ด ํ™•์‹คํžˆ ์•Œ๊ธฐ

๊ณต์‹๋ฌธ์„œ์— ์ด๊ฒƒ์ €๊ฒƒ ์ •๋ฆฌ๊ฐ€ ์ž˜ ๋˜์–ด ์žˆ์–ด์„œ ํ•ต์‹ฌ๋งŒ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•œ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ๋งค์šฐ ๊ธฐ๋ณธ์ ์ธ ๋‚ด์šฉ์ด๋ฉด์„œ๋„ ํ•ต์‹ฌ์ ์ธ ์‚ฌํ•ญ์ด๋ผ ์ƒ๊ฐํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.

ํ™”๋ฉด์€ ์Šคํƒ๊ตฌ์กฐ๋กœ ๋˜์–ด์žˆ๋‹ค. push ๋กœ ์ด๋™ํ•˜๋ฉด ํ˜„์žฌ ํ™”๋ฉด ์Šคํƒ์— ์‹ ๊ทœ ํ™”๋ฉด์„ ์Œ“๋Š” ํ˜•์‹์ด๋‹ค. ๋ฐ˜๋Œ€๋กœ go ๋กœ ์ด๋™ํ•˜๋ฉด ํ˜„์žฌ์˜ ํ™”๋ฉด์„ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด ์›๋ฆฌ์™€ ์—ฐ๊ฒฐํ•˜์—ฌ GoRoute์˜ routes[] ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋‘ฌ์•ผํ•œ๋‹ค. go ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์€ ์Šคํƒ์— ์Œ“๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋…๋ฆฝ์ ์ธ ๋ฃจํŠธ๋ ˆ๋ฒจ๋กœ ๋ผ์šฐํŠธ๊ฐ€ ์กด์žฌํ•ด๋„ ๋œ๋‹ค. ํ•˜์ง€๋งŒ push ๋กœ ์Œ“๋Š” ๊ฒƒ์€ ๋ฐ˜๋“œ์‹œ ์ƒ์œ„ ์œ„์ ฏ์ด ํ•˜์œ„ ์œ„์ ฏ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ ์Œ“์„ ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด A์œ„์ ฏ์—์„œ B์œ„์ ฏ์œผ๋กœ go ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒฝ์šฐ ๋…๋ฆฝ์ ์ธ ๋ฃจํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด๋„ ์ƒ๊ด€์ด ์—†๋‹ค. ํ•˜์ง€๋งŒ A์—์„œ push ๋กœ B๋ฅผ ์Œ“์•„ ์˜ฌ๋ฆฌ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์— A๋Š” ๋ฐ˜๋“œ์‹œ ํ•˜์œ„ ์œ„์ ฏ์œผ๋กœ B๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค. routes ๋ฐฐ์—ด ๋‚ด์— B๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

go_router ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋„˜๊ธฐ๊ธฐ

๋‹จ์ˆœํ•œ ์‚ฌ์šฉ๋ฒ• ์ˆ˜์ค€์ด๋ผ์„œ ํ”„๋กœ์ ํŠธ์— ์‹ค์ œ ๊ตฌํ˜„ํ–ˆ๋˜ ์ฝ”๋“œ๋ฅผ ์˜ˆ์ œ๋กœ ๋‚จ๊ธด๋‹ค.

path ๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

              onPressed: () async {
                String path = _currentSubMenuType == SubMenuType.normalLounge
                    ? RouterLocation.createPost
                    : '${RouterLocation.createPostGroup}/:id';

                final refreshRequired = await context.pushNamed(
                  path,
                  pathParameters: _selectedGroupId != null
                      ? {'id': _selectedGroupId.toString()}
                      : {},
                  extra: _currentSubMenuType == SubMenuType.normalLounge
                      ? PostType.lounge
                      : PostType.group,
                );

                if ((refreshRequired != null) &&
                    ((refreshRequired as List<bool>).first)) {
                  if (_currentSubMenuType == SubMenuType.normalLounge) {
                    Future.microtask(() => context
                        .read<PostBloc>()
                        .add(NormalLoungeSubMenuRequested.initial()));
                  } else {
                    Future.microtask(() => context
                        .read<PostBloc>()
                        .add(GroupLoungeSubMenuRequested.initial()));
                  }
                }
              },
  static final GoRoute createPostGroup = GoRoute(
    path: '${RouterLocation.createPostGroup}/:id',
    name: '${RouterLocation.createPostGroup}/:id',
    builder: (context, state) {
      final String groupId = state.pathParameters['id']!;
      final PostType postType = state.extra as PostType;

      return BlocProvider<PostBloc>(
        create: (_) => PostBloc(
          loadingSpinnerBloc: Injector.loadingSpinnerBloc,
          postRepository: Injector.postRepository,
          groupRepository: Injector.groupRepository,
        ),
        child: CreatePostScreen(
          postType: postType,
          groupId: postType == PostType.group ? int.parse(groupId) : null,
        ),
      );
    },
  );// Some code

queryParameter ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

                      onTap: () async {
                        final result = await context.pushNamed(
                          RouterLocation.subComment,
                          extra: widget.comment,
                          queryParameters: {
                            'postId': widget.postId.toString(),
                            'commentId': widget.comment.id.toString(),
                          },
                        );
                        final refreshRequired = ((result != null) &&
                            ((result as List<bool>).first));

                        if (refreshRequired) {
                          widget.commentInfiniteModel!.initialize();
                          _refreshPost();
                        }
                      },
  static final GoRoute subComment = GoRoute(
    path: RouterLocation.subComment,
    name: RouterLocation.subComment,
    builder: (context, state) {
      final int postId = int.parse(state.queryParameters['postId']!);
      final int commentId = int.parse(state.queryParameters['commentId']!);
      return BlocProvider<CommentBloc>(
        create: (_) => CommentBloc(
          loadingSpinnerBloc: Injector.loadingSpinnerBloc,
          commentRepository: Injector.commentRepository,
        ),
        child: SubCommentScreen(postId: postId, commentId: commentId),
      );
    },
  );

extra ๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

    final rotatedResult = await context.pushNamed(
      RouterLocation.imageRotation,
      extra: File(imageFile.path),
    );
  static final GoRoute imageRotation = GoRoute(
    path: RouterLocation.imageRotation,
    name: RouterLocation.imageRotation,
    builder: (context, state) {
      final File image = state.extra as File;
      return ImageRotationScreen(image: image);
    },
  );

Last updated