ff_annotation_route
Languages: English | 中文简体
Description
Provide a route generator to create route map quickly by annotations.
- ff_annotation_route
Usage
Add packages to dependencies
Add the package to dependencies
in your project/packages's pubspec.yaml
- null-safety
dependencies:
# add for a package
ff_annotation_route_core: any
# add only for a project
ff_annotation_route_library: any
Download with flutter packages get
Add annotation
Empty Constructor
import 'package:ff_annotation_route/ff_annotation_route.dart';
@FFRoute(
name: "fluttercandies://mainpage",
routeName: "MainPage",
)
class MainPage extends StatelessWidget
{
// ...
}
Constructor with arguments
The tool will handle it. What you should take care is that provide import url by setting argumentImports
if it has
class/enum argument.you can use @FFArgumentImport()
instead now.
or you can use --no-fast-mode
for now, it will add parameters refer import automatically.
@FFArgumentImport('hide TestMode2')
import 'package:example1/src/model/test_model.dart';
@FFArgumentImport()
import 'package:example1/src/model/test_model1.dart' hide TestMode3;
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
@FFRoute(
name: 'flutterCandies://testPageE',
routeName: 'testPageE',
description: 'Show how to push new page with arguments(class)',
// argumentImports are still work for some cases which you can't use @FFArgumentImport()
// argumentImports: <String>[
// 'import \'package:example1/src/model/test_model.dart\';',
// 'import \'package:example1/src/model/test_model1.dart\';',
// ],
exts: <String, dynamic>{
'group': 'Complex',
'order': 1,
},
)
class TestPageE extends StatelessWidget {
const TestPageE({
this.testMode = const TestMode(
id: 2,
isTest: false,
),
this.testMode1,
});
factory TestPageE.deafult() => TestPageE(
testMode: TestMode.deafult(),
);
factory TestPageE.required({@required TestMode testMode}) => TestPageE(
testMode: testMode,
);
final TestMode testMode;
final TestMode1 testMode1;
}
FFRoute
Parameter | Description | Default |
---|---|---|
name | The name of the route (e.g., "/settings") | required |
showStatusBar | Whether to show the status bar. | true |
routeName | The route name to track page. | '' |
pageRouteType | The type of page route.(material, cupertino, transparent) | - |
description | The description of the route. | '' |
exts | The extend arguments. | - |
argumentImports | The imports of arguments. For example, class/enum argument should provide import url. you can use @FFArgumentImport() instead now. | - |
codes | to support something can't write in annotation, it will be hadnled as a code when generate route. see | - |
Generate Route File
Environment
Add dart bin into to your $PATH
.
cache\dart-sdk\bin
Activate the plugin
dart pub global activate ff_annotation_route
Execute command
Go to your project's root and execute command.
ff_route <command> [arguments]
Command Parameter
Available commands:
-h, --[no-]help Help usage
-p, --path Flutter project root path
(defaults to ".")
-n, --name Routes constant class name.
(defaults to "Routes")
-o, --output The path of main project route file and helper file.It is relative to the lib directory
-g, --git scan git lib(you should specify package names and split multiple by ,)
--exclude-packages Exclude given packages from scanning
--routes-file-output The path of routes file. It is relative to the lib directory
--const-ignore The regular to ignore some route consts
--[no-]package Is this a package
--[no-]super-arguments Whether generate page arguments helper class
-s, --[no-]save Whether save the arguments into the local
It will execute the local arguments if run "ff_route" without any arguments
--[no-]null-safety enable null-safety
(defaults to on)
--[no-]arguments-case-sensitive arguments is case sensitive or not
(defaults to on)
--[no-]fast-mode fast-mode: only analyze base on single dart file, it's fast.
no-fast mode: analyze base on whole packages and sdk, support super parameters and add parameters refer import automatically.
(defaults to on)
Navigator 1.0
you can see full demo in example
Main.dart
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/material.dart';
import 'example_route.dart';
import 'example_routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: Routes.fluttercandiesMainpage,
onGenerateRoute: (RouteSettings settings) {
return onGenerateRoute(
settings: settings,
getRouteSettings: getRouteSettings,
routeSettingsWrapper: (FFRouteSettings ffRouteSettings) {
if (ffRouteSettings.name == Routes.fluttercandiesMainpage ||
ffRouteSettings.name ==
Routes.fluttercandiesDemogrouppage.name) {
return ffRouteSettings;
}
return ffRouteSettings.copyWith(
widget: CommonWidget(
child: ffRouteSettings.widget,
title: ffRouteSettings.routeName,
));
},
);
},
);
}
}
Push
Push name
Navigator.pushNamed(context, Routes.fluttercandiesMainpage /* fluttercandies://mainpage */);
Push name with arguments
arguments
MUST be aMap<String, dynamic>
Navigator.pushNamed(
context,
Routes.flutterCandiesTestPageE,
arguments: <String, dynamic>{
constructorName: 'required',
'testMode': const TestMode(
id: 100,
isTest: true,
),
},
);
- enable --super-arguments
Navigator.pushNamed(
context,
Routes.flutterCandiesTestPageE.name,
arguments: Routes.flutterCandiesTestPageE.requiredC(
testMode: const TestMode(
id: 100,
isTest: true,
),
),
);
Navigator 2.0
you can see full demo in example1
Main.dart
import 'dart:convert';
import 'package:example1/src/model/test_model.dart';
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'example1_route.dart';
import 'example1_routes.dart';
void main() {
// tool will handle simple types(int,double,bool etc.), but not all of them.
// for example, you can type in web browser
// http://localhost:64916/#flutterCandies://testPageF?list=[4,5,6]&map={"ddd":123}&testMode={"id":2,"isTest":true}
// the queryParameters will be converted base on your case.
FFConvert.convert = <T>(dynamic value) {
if (value == null) {
return null;
}
print(T);
final dynamic output = json.decode(value.toString());
if (<int>[] is T && output is List<dynamic>) {
return output.map<int>((dynamic e) => asT<int>(e)).toList() as T;
} else if (<String, String>{} is T && output is Map<dynamic, dynamic>) {
return output.map<String, String>((dynamic key, dynamic value) =>
MapEntry<String, String>(key.toString(), value.toString())) as T;
} else if (const TestMode() is T && output is Map<dynamic, dynamic>) {
return TestMode.fromJson(output) as T;
}
return json.decode(value.toString()) as T;
};
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final FFRouteInformationParser _ffRouteInformationParser =
FFRouteInformationParser();
final FFRouterDelegate _ffRouterDelegate = FFRouterDelegate(
getRouteSettings: getRouteSettings,
pageWrapper: <T>(FFPage<T> ffPage) {
return ffPage.copyWith(
widget: ffPage.name == Routes.fluttercandiesMainpage ||
ffPage.name == Routes.fluttercandiesDemogrouppage.name
? ffPage.widget
: CommonWidget(
child: ffPage.widget,
routeName: ffPage.routeName,
),
);
},
);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
// initialRoute
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: Routes.fluttercandiesMainpage,
),
),
routeInformationParser: _ffRouteInformationParser,
routerDelegate: _ffRouterDelegate,
);
}
}
FFRouteInformationParser
It's working on Web when you type in browser or report to browser. A delegate that is used by the Router
widget to parse a route information
into a configuration of type RouteSettings
.
for example:
xxx?a=1&b=2
<=> RouteSettings(name:'xxx',arguments:<String, dynamic>{'a':'1','b':'2'})
FFRouterDelegate
A delegate that is used by the Router
widget to build and configure anavigating widget.
It provides push/pop methods like Navigator.
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: Routes.flutterCandiesTestPageF.d(
<int>[1, 2, 3],
map: <String, String>{'ddd': 'dddd'},
testMode: const TestMode(id: 1, isTest: true),
),
);
you can find more demo in test_page_c.dart.
Push
Push name
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageA,
);
Push name with arguments
arguments
MUST be aMap<String, dynamic>
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: Routes.flutterCandiesTestPageF.d(
<int>[1, 2, 3],
map: <String, String>{'ddd': 'dddd'},
testMode: const TestMode(id: 1, isTest: true),
),
);
- enable --super-arguments
FFRouterDelegate.of(context).pushNamed<void>(
Routes.flutterCandiesTestPageF.name,
arguments: <String, dynamic>{
'list': <int>[1, 2, 3],
'map': <String, String>{'ddd': 'dddd'},
'testMode': const TestMode(id: 1, isTest: true),
}
)
GetX
How to use
Getx is supported, you just need to convert FFRouteSettings
to GetPageRoute
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: Routes.fluttercandiesMainpage.name,
onGenerateRoute: (RouteSettings settings) {
FFRouteSettings ffRouteSettings = getRouteSettings(
name: settings.name!,
arguments: settings.arguments as Map<String, dynamic>?,
notFoundPageBuilder: () => Scaffold(
appBar: AppBar(),
body: const Center(
child: Text('not find page'),
),
),
);
Bindings? binding;
if (ffRouteSettings.codes != null) {
binding = ffRouteSettings.codes!['binding'] as Bindings?;
}
Transition? transition;
bool opaque = true;
if (ffRouteSettings.pageRouteType != null) {
switch (ffRouteSettings.pageRouteType) {
case PageRouteType.cupertino:
transition = Transition.cupertino;
break;
case PageRouteType.material:
transition = Transition.downToUp;
break;
case PageRouteType.transparent:
opaque = false;
break;
default:
}
}
return GetPageRoute(
binding: binding,
opaque: opaque,
settings: ffRouteSettings,
transition: transition,
page: () => ffRouteSettings.builder(),
);
},
);
}
}
How to set the parameter of GetPageRoute
for example: 'Bindings' is not const class, so it can't write in annotation, but you can set it as following codes:
- define it in
codes
- add import url in
argumentImports
- get it in
onGenerateRoute
@FFRoute(
name: "/BindingsPage",
routeName: 'BindingsPage',
description: 'how to use Bindings with Annotation.',
codes: <String, String>{
'binding': 'Bindings1()',
},
argumentImports: <String>[
'import \'package:example_getx/src/bindings/bindings1.dart\';'
],
)
onGenerateRoute: (RouteSettings settings) {
FFRouteSettings ffRouteSettings = getRouteSettings(
name: settings.name!,
arguments: settings.arguments as Map<String, dynamic>?,
notFoundPageBuilder: () => Scaffold(
appBar: AppBar(),
body: const Center(
child: Text('not find page'),
),
),
);
Bindings? binding;
if (ffRouteSettings.codes != null) {
binding = ffRouteSettings.codes!['binding'] as Bindings?;
}
Transition? transition;
bool opaque = true;
if (ffRouteSettings.pageRouteType != null) {
switch (ffRouteSettings.pageRouteType) {
case PageRouteType.cupertino:
transition = Transition.cupertino;
break;
case PageRouteType.material:
transition = Transition.downToUp;
break;
case PageRouteType.transparent:
opaque = false;
break;
default:
}
}
return GetPageRoute(
binding: binding,
opaque: opaque,
settings: ffRouteSettings,
transition: transition,
page: () => ffRouteSettings.builder(),
);
},
Functional Widget
How to use with functional_widget?
@swidget
@FFRoute(
name: 'flutterCandies://func1',
routeName: 'test-func-1',
)
Widget func1(
int a,
String? b, {
bool? c,
required double d,
}) {
return Container();
}
Simple code is here.
Code Hints
you can use route as 'Routes.flutterCandiesTestPageE', and see Code Hints from ide.
- default
/// 'This is test page E.'
///
/// [name] : 'flutterCandies://testPageE'
///
/// [routeName] : 'testPageE'
///
/// [description] : 'This is test page E.'
///
/// [constructors] :
///
/// TestPageE : [TestMode testMode, TestMode1 testMode1]
///
/// TestPageE.deafult : []
///
/// TestPageE.required : [TestMode(required) testMode]
///
/// [exts] : {group: Complex, order: 1}
static const String flutterCandiesTestPageE = 'flutterCandies://testPageE';
- enable --super-arguments
/// 'This is test page E.'
///
/// [name] : 'flutterCandies://testPageE'
///
/// [routeName] : 'testPageE'
///
/// [description] : 'This is test page E.'
///
/// [constructors] :
///
/// TestPageE : [TestMode testMode, TestMode1 testMode1]
///
/// TestPageE.test : []
///
/// TestPageE.requiredC : [TestMode(required) testMode]
///
/// [exts] : {group: Complex, order: 1}
static const _FlutterCandiesTestPageE flutterCandiesTestPageE =
_FlutterCandiesTestPageE();
class _FlutterCandiesTestPageE {
const _FlutterCandiesTestPageE();
String get name => 'flutterCandies://testPageE';
Map<String, dynamic> d(
{TestMode testMode = const TestMode(id: 2, isTest: false),
TestMode1 testMode1}) =>
<String, dynamic>{
'testMode': testMode,
'testMode1': testMode1,
};
Map<String, dynamic> test() => const <String, dynamic>{
'constructorName': 'test',
};
Map<String, dynamic> requiredC({@required TestMode testMode}) =>
<String, dynamic>{
'testMode': testMode,
'constructorName': 'requiredC',
};
@override
String toString() => name;
}