|
1 | | -[](https://github.com/dev-cetera) |
| 1 | +[](https://github.com/dev-cetera) |
2 | 2 |
|
3 | 3 | [](https://pub.dev/packages/df_router) |
4 | | -[](https://github.com/dev-cetera/df_router/tree/v0.5.4) |
| 4 | +[](https://github.com/dev-cetera/df_router/tree/v0.5.5) |
5 | 5 | [](https://www.buymeacoffee.com/dev_cetera) |
6 | 6 | [](https://github.com/sponsors/dev-cetera) |
7 | | -[](https://www.patreon.com/t0mb3rr) |
| 7 | +[](https://www.patreon.com/robelator) |
8 | 8 | [](https://discord.gg/gEQ8y2nfyX) |
9 | 9 | [](https://www.instagram.com/dev_cetera/) |
10 | 10 | [](https://raw.githubusercontent.com/dev-cetera/df_router/main/LICENSE) |
|
15 | 15 |
|
16 | 16 | ## Summary |
17 | 17 |
|
18 | | -A lightweight router designed for ease of use and efficient state management. Explore it in action with this live web app: https://dev-cetera.github.io/df_router/chat?chatId=123456. |
| 18 | +A lightweight Flutter router with state management. [Live demo](https://dev-cetera.github.io/df_router/chat?chatId=123456). |
19 | 19 |
|
20 | | -## Features |
21 | | - |
22 | | -- **Declarative Routing:** Define your routes and their corresponding widgets in a clean, list-based manner. |
23 | | -- **Stateful Routes:** `RouteState` objects represent unique routes, including paths, query parameters, and strongly-typed `extra` data. |
24 | | -- **Widget Caching & Preservation:** Control whether route widgets are preserved in memory (`shouldPreserve`) or pre-built (`shouldPrebuild`) for performance. |
25 | | -- **Customizable Transitions:** Easily define custom page transitions called "effects" or use the provided `MaterialEffect` and `CupertinoEffect`. |
26 | | -- **Persistent UI Wrapper:** Add common UI elements like headers, footers, or navigation bars that persist across route changes. |
27 | | -- **Easy Navigation:** Navigate using `RouteState` objects or simple path strings. |
28 | | -- **Typed `extra` Data:** Pass strongly-typed data between routes. |
29 | | - |
30 | | -## Usage |
31 | | - |
32 | | -### 1. Define Your RouteStates |
33 | | - |
34 | | -Create classes that extend `RouteState` for each distinct route in your application. These classes encapsulate the path and can manage query parameters. |
| 20 | +## Quick Start |
35 | 21 |
|
36 | 22 | ```dart |
37 | | -final class HomeRouteState extends RouteState { |
38 | | - HomeRouteState() |
39 | | - : super.parse( |
40 | | - '/home', |
41 | | - // Use QuickForwardtEffect() as the default transtion effect for this |
42 | | - // route. This can be overridden when pushing this route. |
43 | | - animationEffect: const QuickForwardEffect(), |
44 | | - ); |
45 | | -} |
| 23 | +import 'package:df_router/df_router.dart'; |
| 24 | +import 'package:flutter/material.dart'; |
46 | 25 |
|
47 | | -// This route is only used in the RouteManager, so it does not need to |
48 | | -// be pushed directly. It is a base route for the chat feature. |
49 | | -final class BaseChatRouteState extends RouteState { |
50 | | - BaseChatRouteState({Map<String, String>? queryParameters}) |
51 | | - : super.parse( |
52 | | - '/chat', |
53 | | - queryParameters: queryParameters, |
54 | | - // Use a different animation effect for this route. |
55 | | - animationEffect: const SlideDownEffect(), |
56 | | - ); |
| 26 | +void main() => runApp(const MyApp()); |
57 | 27 |
|
58 | | - BaseChatRouteState.from(RouteState other) : super(other.uri); |
| 28 | +// 1. Define routes. |
| 29 | +final class HomeRoute extends RouteState { |
| 30 | + HomeRoute() : super.parse('/home'); |
59 | 31 | } |
60 | 32 |
|
61 | | -final class ChatRouteState extends BaseChatRouteState { |
| 33 | +final class ChatRoute extends RouteState { |
62 | 34 | final String chatId; |
63 | | -
|
64 | | - ChatRouteState({required this.chatId}) : super(queryParameters: {'chatId': chatId}); |
65 | | -
|
66 | | - ChatRouteState.from(super.other) |
| 35 | + ChatRoute({required this.chatId}) |
| 36 | + : super.parse('/chat', queryParameters: {'chatId': chatId}); |
| 37 | + ChatRoute.from(RouteState other) |
67 | 38 | : chatId = other.uri.queryParameters['chatId'] ?? '', |
68 | | - super.from(); |
| 39 | + super(other.uri); |
69 | 40 | } |
70 | | -``` |
71 | | - |
72 | | -### 2. Configure RouteStateManager |
73 | 41 |
|
74 | | -In your MaterialApp (or CupertinoApp), use the RouteStateManager widget to define your application's routing configuration. |
75 | | - |
76 | | -```dart |
| 42 | +// 2. Setup RouteManager. |
77 | 43 | class MyApp extends StatelessWidget { |
78 | 44 | const MyApp({super.key}); |
79 | 45 |
|
80 | 46 | @override |
81 | 47 | Widget build(BuildContext context) { |
82 | 48 | return MaterialApp( |
83 | | - //home: // Do not use "home", as it conflicts with RouteManager. Use |
84 | | - // "builder" instead. |
85 | 49 | builder: (context, child) { |
86 | 50 | return RouteManager( |
87 | | - fallbackRouteState: () => HomeRouteState(), |
| 51 | + fallbackRouteState: HomeRoute.new, |
88 | 52 | builders: [ |
89 | 53 | RouteBuilder( |
90 | | - routeState: HomeRouteState(), |
91 | | - // Pre-build the HomeScreen even if the initial route is not |
92 | | - // HomeRouteState. This is useful for performance optimization. |
93 | | - shouldPrebuild: true, |
94 | | - // Preserve the HomeScreen widget to avoid rebuilding it. |
95 | | - shouldPreserve: true, |
96 | | - builder: (context, routeState) => HomeScreen(routeState: HomeRouteState()), |
| 54 | + routeState: HomeRoute(), |
| 55 | + builder: (context, state) => const HomeScreen(), |
97 | 56 | ), |
98 | 57 | RouteBuilder( |
99 | | - // Use the BaseChatRouteState instead of the ChatRouteState |
100 | | - // since it does not require a chatId to be pushed. |
101 | | - routeState: BaseChatRouteState(), |
102 | | - builder: |
103 | | - (context, routeState) => ChatScreen(routeState: ChatRouteState.from(routeState)), |
| 58 | + routeState: ChatRoute(chatId: ''), |
| 59 | + builder: (context, state) => ChatScreen(route: ChatRoute.from(state)), |
104 | 60 | ), |
105 | 61 | ], |
106 | 62 | ); |
107 | 63 | }, |
108 | 64 | ); |
109 | 65 | } |
110 | 66 | } |
111 | | -``` |
112 | | - |
113 | | -### 3. Create Your Screen Widgets |
114 | | - |
115 | | -Your screen widgets should use the `RouteWidgetMixin` to easily access the current `RouteState`. |
116 | 67 |
|
117 | | -```dart |
118 | | -class HomeScreen extends StatelessWidget with RouteWidgetMixin { |
119 | | - @override |
120 | | - final HomeRouteState? routeState; |
121 | | - const HomeScreen({super.key, this.routeState}); |
122 | | -
|
123 | | - @override |
124 | | - Widget build(BuildContext context) { |
125 | | - return Scaffold( |
126 | | - appBar: AppBar(title: const Text('Home')), |
127 | | - backgroundColor: Colors.green, |
128 | | - body: Center( |
129 | | - child: ElevatedButton( |
130 | | - onPressed: () { |
131 | | - final controller = RouteController.of(context); |
132 | | - controller.push( |
133 | | - ChatRouteState(chatId: '123456'), |
134 | | - // Override the default animation effect for this push. |
135 | | - animationEffect: const CupertinoEffect(), |
136 | | - ); |
137 | | - }, |
138 | | - child: const Text('Go to Chat'), |
139 | | - ), |
140 | | - ), |
141 | | - ); |
142 | | - } |
143 | | -} |
144 | | -
|
145 | | -class ChatScreen extends StatelessWidget with RouteWidgetMixin { |
146 | | - @override |
147 | | - final ChatRouteState? routeState; |
148 | | -
|
149 | | - const ChatScreen({super.key, this.routeState}); |
150 | | -
|
151 | | - @override |
152 | | - Widget build(BuildContext context) { |
153 | | - return Scaffold( |
154 | | - appBar: AppBar(title: Text('Chat - ${routeState?.chatId}')), |
155 | | - backgroundColor: Colors.blue, |
156 | | - body: Center( |
157 | | - child: Column( |
158 | | - mainAxisAlignment: MainAxisAlignment.center, |
159 | | - children: [ |
160 | | - ElevatedButton( |
161 | | - onPressed: () { |
162 | | - final controller = RouteController.of(context); |
163 | | - controller.goBackward(); |
164 | | - }, |
165 | | - child: const Text('Go Back - Default Effect'), |
166 | | - ), |
167 | | - ElevatedButton( |
168 | | - onPressed: () { |
169 | | - final controller = RouteController.of(context); |
170 | | - controller.goBackward(animationEffect: const QuickBackEffect()); |
171 | | - }, |
172 | | - child: const Text('Go Back - Quick Back Effect'), |
173 | | - ), |
174 | | - ElevatedButton( |
175 | | - onPressed: () { |
176 | | - final controller = RouteController.of(context); |
177 | | - controller.push(HomeRouteState()); |
178 | | - }, |
179 | | - child: const Text('Go Home - Default Effect'), |
180 | | - ), |
181 | | - ElevatedButton( |
182 | | - onPressed: () { |
183 | | - final controller = RouteController.of(context); |
184 | | - controller.push(HomeRouteState().copyWith(animationEffect: const MaterialEffect())); |
185 | | - }, |
186 | | - child: const Text('Go Home - Material Effect'), |
187 | | - ), |
188 | | - ElevatedButton( |
189 | | - onPressed: () { |
190 | | - final controller = RouteController.of(context); |
191 | | - controller.push(HomeRouteState(), animationEffect: const PageFlapDown()); |
192 | | - }, |
193 | | - child: const Text('Go Home - Page Flap Down Effect'), |
194 | | - ), |
195 | | - ], |
196 | | - ), |
197 | | - ), |
198 | | - ); |
199 | | - } |
200 | | -} |
| 68 | +// 3. Navigate. |
| 69 | +RouteController.of(context).push(ChatRoute(chatId: '123')); |
| 70 | +RouteController.of(context).goBackward(); |
201 | 71 | ``` |
202 | 72 |
|
203 | | -## 4. Navigation |
204 | | - |
205 | | -Access the RouteStateController to navigate: |
206 | | - |
207 | | -```dart |
208 | | -final controller = RouteStateController.of(context); |
209 | | -
|
210 | | -// Push the home screen with a material effect. |
211 | | -controller.push(HomeRouteState(), animationEffect: const CupertinoEffect()); |
212 | | -controller.push(HomeRouteState().copyWith(animationEffect: const MaterialEffect())); |
213 | | -
|
214 | | -// Go back. |
215 | | -controller.pushBack(); |
216 | | -
|
217 | | -// Remove a specific route to free up resources. |
218 | | -controller.removeStatesFromCache([HomeRouteState()]); |
219 | | -
|
220 | | -// Clear the entire widget cache. |
221 | | -controller.clearCache([HomeRouteState()]); |
222 | | -
|
223 | | -// Add a route to the cache without navigating. This will preload it. |
224 | | -controller.addToCache([HomeRouteState()]); |
225 | | -
|
226 | | -// Reset the cache to its initial state. This honours the shouldPrebuild property. |
227 | | -controller.resetState(); |
228 | | -
|
229 | | -// Get the current state, e.g. HomeRouteState(); |
230 | | -final current = controller.current; |
| 73 | +## Features |
231 | 74 |
|
232 | | -// Get the requested state, which is what was typed in the URL bar, e.g. "/home?query=123" may return an instance of HomeRouteState(). This will return nul if the URL does not match any defined route. |
233 | | -final requested = controller.requested; |
234 | | -``` |
| 75 | +- Declarative route definitions |
| 76 | +- Query parameter support |
| 77 | +- Typed route data via `extra` |
| 78 | +- Widget caching with `shouldPreserve` and `shouldPrebuild` |
| 79 | +- Custom transitions (`MaterialEffect`, `CupertinoEffect`, etc.) |
235 | 80 |
|
236 | 81 | <!-- END _README_CONTENT --> |
237 | 82 |
|
238 | 83 | --- |
239 | 84 |
|
240 | | -🔍 For more information, refer to the [API reference](https://pub.dev/documentation/df_router/). |
241 | | - |
242 | | ---- |
243 | | - |
244 | | -## 💬 Contributing and Discussions |
245 | | - |
246 | | -This is an open-source project, and we warmly welcome contributions from everyone, regardless of experience level. Whether you're a seasoned developer or just starting out, contributing to this project is a fantastic way to learn, share your knowledge, and make a meaningful impact on the community. |
247 | | - |
248 | | -### ☝️ Ways you can contribute |
249 | | - |
250 | | -- **Find us on Discord:** Feel free to ask questions and engage with the community here: https://discord.gg/gEQ8y2nfyX. |
251 | | -- **Share your ideas:** Every perspective matters, and your ideas can spark innovation. |
252 | | -- **Help others:** Engage with other users by offering advice, solutions, or troubleshooting assistance. |
253 | | -- **Report bugs:** Help us identify and fix issues to make the project more robust. |
254 | | -- **Suggest improvements or new features:** Your ideas can help shape the future of the project. |
255 | | -- **Help clarify documentation:** Good documentation is key to accessibility. You can make it easier for others to get started by improving or expanding our documentation. |
256 | | -- **Write articles:** Share your knowledge by writing tutorials, guides, or blog posts about your experiences with the project. It's a great way to contribute and help others learn. |
257 | | - |
258 | | -No matter how you choose to contribute, your involvement is greatly appreciated and valued! |
259 | | - |
260 | | -### ☕ We drink a lot of coffee... |
| 85 | +For more details, see the [API reference](https://pub.dev/documentation/df_router/). |
261 | 86 |
|
262 | | -If you're enjoying this package and find it valuable, consider showing your appreciation with a small donation. Every bit helps in supporting future development. You can donate here: https://www.buymeacoffee.com/dev_cetera |
| 87 | +## Contributing |
263 | 88 |
|
264 | | -<a href="https://www.buymeacoffee.com/dev_cetera" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" height="40"></a> |
| 89 | +Join our [Discord](https://discord.gg/gEQ8y2nfyX) or support us on [Buy Me A Coffee](https://www.buymeacoffee.com/dev_cetera). |
265 | 90 |
|
266 | | -## LICENSE |
| 91 | +## License |
267 | 92 |
|
268 | | -This project is released under the [MIT License](https://raw.githubusercontent.com/dev-cetera/df_router/main/LICENSE). See [LICENSE](https://raw.githubusercontent.com/dev-cetera/df_router/main/LICENSE) for more information. |
| 93 | +[MIT License](https://raw.githubusercontent.com/dev-cetera/df_router/main/LICENSE) |
0 commit comments