Skip to content

Commit 5edd77a

Browse files
committed
fix: build routes refactor and tests added
1 parent 5e44123 commit 5edd77a

File tree

26 files changed

+385
-146
lines changed

26 files changed

+385
-146
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import 'package:flutter/widgets.dart';
21
import 'package:flutter_news_example/article/article.dart';
32
import 'package:flutter_news_example/home/home.dart';
43
import 'package:flutter_news_example/login/login.dart';
@@ -10,107 +9,56 @@ import 'package:flutter_news_example/slideshow/slideshow.dart';
109
import 'package:flutter_news_example/subscriptions/view/manage_subscription_page.dart';
1110
import 'package:flutter_news_example/user_profile/user_profile.dart';
1211
import 'package:go_router/go_router.dart';
13-
import 'package:news_blocks/news_blocks.dart';
1412

1513
final GoRouter router = GoRouter(
1614
routes: <RouteBase>[
1715
GoRoute(
1816
path: HomePage.routePath,
19-
builder: (BuildContext context, GoRouterState state) {
20-
return const HomePage();
21-
},
17+
builder: HomePage.routeBuilder,
2218
routes: <RouteBase>[
2319
GoRoute(
2420
name: NetworkErrorPage.routePath,
2521
path: NetworkErrorPage.routePath,
26-
builder: (BuildContext context, GoRouterState state) {
27-
final onRetry = state.extra as VoidCallback?;
28-
return NetworkError(onRetry: onRetry);
29-
},
22+
builder: NetworkErrorPage.routeBuilder,
3023
),
3124
GoRoute(
3225
name: LoginWithEmailPage.routePath,
3326
path: LoginWithEmailPage.routePath,
34-
builder: (BuildContext context, GoRouterState state) {
35-
return const LoginWithEmailPage();
36-
},
27+
builder: LoginWithEmailPage.routeBuilder,
3728
routes: <RouteBase>[
3829
GoRoute(
3930
name: MagicLinkPromptPage.routePath,
4031
path: MagicLinkPromptPage.routePath,
41-
builder: (BuildContext context, GoRouterState state) {
42-
return MagicLinkPromptPage(
43-
email: state.uri.queryParameters['email']!,
44-
);
45-
},
32+
builder: MagicLinkPromptPage.routeBuilder,
4633
),
4734
],
4835
),
4936
GoRoute(
5037
name: ArticlePage.routeName,
5138
path: ArticlePage.routePath,
52-
builder: (BuildContext context, GoRouterState state) {
53-
final id = state.pathParameters['id'];
54-
55-
final isVideoArticle = bool.tryParse(
56-
state.uri.queryParameters['isVideoArticle'] ?? 'false',
57-
) ??
58-
false;
59-
final interstitialAdBehavior =
60-
state.uri.queryParameters['interstitialAdBehavior'] != null
61-
? InterstitialAdBehavior.values.firstWhere(
62-
(e) =>
63-
e.toString() ==
64-
'InterstitialAdBehavior.'
65-
// ignore: lines_longer_than_80_chars
66-
'${state.uri.queryParameters['interstitialAdBehavior']}',
67-
)
68-
: null;
69-
70-
if (id == null) {
71-
throw Exception('Missing required "id" parameter');
72-
}
73-
74-
return ArticlePage(
75-
id: id,
76-
isVideoArticle: isVideoArticle,
77-
interstitialAdBehavior:
78-
interstitialAdBehavior ?? InterstitialAdBehavior.onOpen,
79-
);
80-
},
39+
builder: ArticlePage.routeBuilder,
8140
routes: <RouteBase>[
8241
GoRoute(
8342
name: SlideshowPage.routePath,
8443
path: SlideshowPage.routePath,
85-
builder: (BuildContext context, GoRouterState state) {
86-
return SlideshowPage(
87-
slideshow: state.extra! as SlideshowBlock,
88-
articleId: state.pathParameters['id']!,
89-
);
90-
},
44+
builder: SlideshowPage.routeBuilder,
9145
),
9246
],
9347
),
9448
GoRoute(
9549
name: UserProfilePage.routePath,
9650
path: UserProfilePage.routePath,
97-
builder: (BuildContext context, GoRouterState state) {
98-
return const UserProfilePage();
99-
},
51+
builder: UserProfilePage.routeBuilder,
10052
routes: <RouteBase>[
10153
GoRoute(
10254
name: ManageSubscriptionPage.routePath,
10355
path: ManageSubscriptionPage.routePath,
104-
builder: (BuildContext context, GoRouterState state) {
105-
return const ManageSubscriptionPage();
106-
},
56+
builder: ManageSubscriptionPage.routeBuilder,
10757
),
10858
GoRoute(
10959
name: NotificationPreferencesPage.routePath,
11060
path: NotificationPreferencesPage.routePath,
111-
builder: (BuildContext context, GoRouterState state) {
112-
return const NotificationPreferencesPage();
113-
},
61+
builder: NotificationPreferencesPage.routeBuilder,
11462
),
11563
],
11664
),
@@ -119,9 +67,7 @@ final GoRouter router = GoRouter(
11967
GoRoute(
12068
name: OnboardingPage.routePath,
12169
path: OnboardingPage.routePath,
122-
builder: (BuildContext context, GoRouterState state) {
123-
return const OnboardingPage();
124-
},
70+
builder: OnboardingPage.routeBuilder,
12571
),
12672
],
12773
);

flutter_news_example/lib/article/bloc/article_bloc.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import 'package:json_annotation/json_annotation.dart';
1010
import 'package:news_blocks/news_blocks.dart';
1111
import 'package:share_launcher/share_launcher.dart';
1212

13+
part 'article_bloc.g.dart';
1314
part 'article_event.dart';
1415
part 'article_state.dart';
15-
part 'article_bloc.g.dart';
1616

1717
class ArticleBloc extends HydratedBloc<ArticleEvent, ArticleState> {
1818
ArticleBloc({

flutter_news_example/lib/article/view/article_page.dart

+34
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:flutter_news_example/app/app.dart';
88
import 'package:flutter_news_example/article/article.dart';
99
import 'package:flutter_news_example/l10n/l10n.dart';
1010
import 'package:flutter_news_example/subscriptions/subscriptions.dart';
11+
import 'package:go_router/go_router.dart';
1112
import 'package:news_blocks_ui/news_blocks_ui.dart';
1213
import 'package:share_launcher/share_launcher.dart';
1314

@@ -31,6 +32,39 @@ class ArticlePage extends StatelessWidget {
3132
static const routeName = 'article';
3233
static const routePath = 'article/:id';
3334

35+
static Widget routeBuilder(
36+
BuildContext context,
37+
GoRouterState state,
38+
) {
39+
final id = state.pathParameters['id'];
40+
41+
final isVideoArticle = bool.tryParse(
42+
state.uri.queryParameters['isVideoArticle'] ?? 'false',
43+
) ??
44+
false;
45+
final interstitialAdBehavior =
46+
state.uri.queryParameters['interstitialAdBehavior'] != null
47+
? InterstitialAdBehavior.values.firstWhere(
48+
(e) =>
49+
e.toString() ==
50+
'InterstitialAdBehavior.'
51+
// ignore: lines_longer_than_80_chars
52+
'${state.uri.queryParameters['interstitialAdBehavior']}',
53+
)
54+
: null;
55+
56+
if (id == null) {
57+
throw Exception('Missing required "id" parameter');
58+
}
59+
60+
return ArticlePage(
61+
id: id,
62+
isVideoArticle: isVideoArticle,
63+
interstitialAdBehavior:
64+
interstitialAdBehavior ?? InterstitialAdBehavior.onOpen,
65+
);
66+
}
67+
3468
/// The id of the requested article.
3569
final String id;
3670

flutter_news_example/lib/article/widgets/article_content.dart

+5-6
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,14 @@ class ArticleContent extends StatelessWidget {
3535

3636
return ArticleContentSeenListener(
3737
child: BlocListener<ArticleBloc, ArticleState>(
38-
listener: (context, state) {
38+
listener: (context, state) async {
3939
if (state.status == ArticleStatus.failure && state.content.isEmpty) {
40-
context.goNamed(
40+
await context.pushNamed(
4141
NetworkErrorPage.routePath,
42-
extra: () {
43-
context.read<ArticleBloc>().add(const ArticleRequested());
44-
Navigator.of(context).pop();
45-
},
4642
);
43+
if (context.mounted) {
44+
context.read<ArticleBloc>().add(const ArticleRequested());
45+
}
4746
} else if (state.status == ArticleStatus.shareFailure) {
4847
_handleShareFailure(context);
4948
}

flutter_news_example/lib/feed/widgets/category_feed.dart

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ class CategoryFeed extends StatelessWidget {
3030
.select((FeedBloc bloc) => bloc.state.status == FeedStatus.failure);
3131

3232
return BlocListener<FeedBloc, FeedState>(
33-
listener: (context, state) {
33+
listener: (context, state) async {
3434
if (state.status == FeedStatus.failure && state.feed.isEmpty) {
35-
context.goNamed(
35+
await context.pushNamed(
3636
NetworkErrorPage.routePath,
37-
extra: () {
38-
context.read<FeedBloc>().add(FeedRequested(category: category));
39-
Navigator.of(context).pop();
40-
},
4137
);
38+
// TODO: check if this implementation works (tests)
39+
if (context.mounted) {
40+
context.read<FeedBloc>().add(FeedRequested(category: category));
41+
}
4242
}
4343
},
4444
child: RefreshIndicator(

flutter_news_example/lib/home/view/home_page.dart

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:flutter_news_example/feed/feed.dart';
44
import 'package:flutter_news_example/home/home.dart';
5+
import 'package:go_router/go_router.dart';
56
import 'package:news_repository/news_repository.dart';
67

78
class HomePage extends StatelessWidget {
89
const HomePage({super.key});
910

1011
static const routePath = '/';
1112

12-
static Page<void> page() => const MaterialPage<void>(child: HomePage());
13+
static Widget routeBuilder(
14+
BuildContext context,
15+
GoRouterState state,
16+
) =>
17+
const HomePage();
1318

1419
@override
1520
Widget build(BuildContext context) {

flutter_news_example/lib/login/view/login_with_email_page.dart

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ import 'package:app_ui/app_ui.dart';
22
import 'package:flutter/material.dart';
33
import 'package:flutter_bloc/flutter_bloc.dart';
44
import 'package:flutter_news_example/login/login.dart';
5+
import 'package:go_router/go_router.dart';
56
import 'package:user_repository/user_repository.dart';
67

78
class LoginWithEmailPage extends StatelessWidget {
89
const LoginWithEmailPage({super.key});
910

1011
static const routePath = 'login-with-email';
1112

12-
static Route<void> route() =>
13-
MaterialPageRoute<void>(builder: (_) => const LoginWithEmailPage());
13+
static Widget routeBuilder(
14+
BuildContext context,
15+
GoRouterState state,
16+
) =>
17+
const LoginWithEmailPage();
1418

1519
@override
1620
Widget build(BuildContext context) {

flutter_news_example/lib/magic_link_prompt/view/magic_link_prompt_page.dart

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:app_ui/app_ui.dart';
22
import 'package:flutter/material.dart';
33
import 'package:flutter_news_example/login/login.dart';
44
import 'package:flutter_news_example/magic_link_prompt/magic_link_prompt.dart';
5+
import 'package:go_router/go_router.dart';
56

67
class MagicLinkPromptPage extends StatelessWidget {
78
const MagicLinkPromptPage({required this.email, super.key});
@@ -10,6 +11,14 @@ class MagicLinkPromptPage extends StatelessWidget {
1011

1112
final String email;
1213

14+
static Widget routeBuilder(
15+
BuildContext context,
16+
GoRouterState state,
17+
) {
18+
final email = state.uri.queryParameters['email']!;
19+
return MagicLinkPromptPage(email: email);
20+
}
21+
1322
@override
1423
Widget build(BuildContext context) {
1524
return Scaffold(

flutter_news_example/lib/network_error/view/network_error.dart

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
11
import 'package:app_ui/app_ui.dart';
22
import 'package:flutter/material.dart';
33
import 'package:flutter_news_example/l10n/l10n.dart';
4+
import 'package:go_router/go_router.dart';
45

56
/// {@template network_error}
67
/// A network error alert page.
78
/// {@endtemplate}
89
class NetworkErrorPage extends StatelessWidget {
910
/// {@macro network_error}
10-
const NetworkErrorPage({super.key, this.onRetry});
11-
12-
/// An optional callback which is invoked when the retry button is pressed.
13-
final VoidCallback? onRetry;
11+
const NetworkErrorPage({
12+
super.key,
13+
});
1414

1515
static const routePath = 'network-error';
1616

17+
static Widget routeBuilder(
18+
BuildContext context,
19+
GoRouterState state,
20+
) =>
21+
const NetworkErrorPage();
22+
1723
@override
1824
Widget build(BuildContext context) {
1925
return Scaffold(
2026
backgroundColor: AppColors.background,
2127
appBar: AppBar(leading: const AppBackButton()),
22-
body: Center(
23-
child: NetworkError(onRetry: onRetry),
28+
body: const Center(
29+
child: NetworkError(),
2430
),
2531
);
2632
}
@@ -59,7 +65,7 @@ class NetworkError extends StatelessWidget {
5965
Padding(
6066
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xxxlg),
6167
child: AppButton.darkAqua(
62-
onPressed: onRetry,
68+
onPressed: onRetry ?? context.pop,
6369
child: Row(
6470
mainAxisAlignment: MainAxisAlignment.center,
6571
children: [

flutter_news_example/lib/notification_preferences/view/notification_preferences_page.dart

+7-5
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ import 'package:flutter/material.dart';
33
import 'package:flutter_bloc/flutter_bloc.dart';
44
import 'package:flutter_news_example/l10n/l10n.dart';
55
import 'package:flutter_news_example/notification_preferences/notification_preferences.dart';
6+
import 'package:go_router/go_router.dart';
67
import 'package:news_repository/news_repository.dart';
78
import 'package:notifications_repository/notifications_repository.dart';
89

910
class NotificationPreferencesPage extends StatelessWidget {
1011
const NotificationPreferencesPage({super.key});
1112

1213
static const routePath = 'notification-preferences';
13-
static MaterialPageRoute<void> route() {
14-
return MaterialPageRoute(
15-
builder: (_) => const NotificationPreferencesPage(),
16-
);
17-
}
14+
15+
static Widget routeBuilder(
16+
BuildContext context,
17+
GoRouterState state,
18+
) =>
19+
const NotificationPreferencesPage();
1820

1921
@override
2022
Widget build(BuildContext context) {

flutter_news_example/lib/onboarding/view/onboarding_page.dart

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ import 'package:app_ui/app_ui.dart';
33
import 'package:flutter/material.dart';
44
import 'package:flutter_bloc/flutter_bloc.dart';
55
import 'package:flutter_news_example/onboarding/onboarding.dart';
6+
import 'package:go_router/go_router.dart';
67
import 'package:notifications_repository/notifications_repository.dart';
78

89
class OnboardingPage extends StatelessWidget {
910
const OnboardingPage({super.key});
1011

1112
static const routePath = '/onboarding';
1213

13-
static Page<void> page() => const MaterialPage<void>(child: OnboardingPage());
14+
static Widget routeBuilder(
15+
BuildContext context,
16+
GoRouterState state,
17+
) =>
18+
const OnboardingPage();
1419

1520
@override
1621
Widget build(BuildContext context) {

0 commit comments

Comments
 (0)