字节笔记本字节笔记本

Getx router 使用小抄

2022-06-14

《Getx路由使用小抄》详细介绍了如何在Flutter应用中使用Getx库进行路由管理,包括无命名路由和命名路由的导航、数据传递、动态URL链接、中间件以及无上下文的SnackBars、Dialogs和BottomSheets的使用。

Route Management

This is the complete explanation of all there is to Getx when the matter is route management.

How to use

Add this to your pubspec.yaml file:

dependencies:
  get:

If you are going to use routes/snackbars/dialogs/bottomsheets without context, or use the high-level Get APIs, you need to simply add "Get" before your MaterialApp, turning it into GetMaterialApp and enjoy!

GetMaterialApp( // Before: MaterialApp(
  home: MyHome(),
)

To navigate to a new screen: // 直接to到一个组件类名

Get.to(NextScreen());

To close snackbars, dialogs, bottomsheets, or anything you would normally close with Navigator.pop(context); // 返回

Get.back();

To go to the next screen and no option to go back to the previous screen (for use in SplashScreens, login screens and etc.) // 有去无回

Get.off(NextScreen());

To go to the next screen and cancel all previous routes (useful in shopping carts, polls, and tests) // 清空堆栈

Get.offAll(NextScreen());

To navigate to the next route, and receive or update data as soon as you return from it: // 不太知道返回的data 是什么, 大意就是接受和更新数据

var data = await Get.to(Payment());

on other screen, send a data for previous route: // 返回的时候带上货

Get.back(result: 'success');

And use it:

ex (前妻) :

if(data == 'success') madeAnything();

Don't you want to learn our syntax? Just change the Navigator ( uppercase (大写) ) to navigator ( lowercase (小写) ), and you will have all the functions of the standard navigation, without having to use context Example:

// Default Flutter navigator
Navigator.of(context).push(
  context,
  MaterialPageRoute(
    builder: (BuildContext context) {
      return HomePage();
    },
  ),
);

// Get using Flutter syntax without needing context
navigator.push(
  MaterialPageRoute(
    builder: (_) {
      return HomePage();
    },
  ),
);

// Get syntax (It is much better, but you have the right to disagree)
Get.to(HomePage());
  • If you prefer to navigate by namedRoutes, Get also supports this.

To navigate to nextScreen

Get.toNamed("/NextScreen");

To navigate and remove the previous screen from the tree.

Get.offNamed("/NextScreen");

To navigate and remove all previous screens from the tree.

Get.offAllNamed("/NextScreen");

To define routes, use GetMaterialApp:

void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => MyHomePage()),
        GetPage(name: '/second', page: () => Second()),
        GetPage(
          name: '/third',
          page: () => Third(),
          transition: Transition.zoom  
        ),
      ],
    )
  );
}

To handle navigation to non-defined routes (404 error), you can define an unknownRoute page in GetMaterialApp. // 规范写法

void main() {
  runApp(
    GetMaterialApp(
      unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => MyHomePage()),
        GetPage(name: '/second', page: () => Second()),
      ],
    )
  );
}

Send data to named Routes

Just send what you want for arguments. Get accepts anything here, whether it is a String, a Map, a List, or even a class instance. // 带参数

Get.toNamed("/NextScreen", arguments: 'Get is the best');

on your class or controller:

print(Get.arguments);
//print out: Get is the best

Get offer advanced dynamic urls just like on the Web. Web developers have probably already wanted this feature on Flutter, and most likely have seen a package promise this feature and deliver a totally different syntax than a URL would have on web, but Get also solves that. // 获取参数

Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");

on your controller/bloc/stateful/stateless class:

print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo

You can also receive NamedParameters with Get easily:

void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/',
      getPages: [
      GetPage(
        name: '/',
        page: () => MyHomePage(),
      ),
      GetPage(
        name: '/profile/',
        page: () => MyProfile(),
      ),
       //You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
       GetPage(
        name: '/profile/:user',
        page: () => UserProfile(),
      ),
      GetPage(
        name: '/third',
        page: () => Third(),
        transition: Transition.cupertino  
      ),
     ],
    )
  );
}

Send data on route name

Get.toNamed("/profile/34954");

On second screen take the data by parameter

print(Get.parameters['user']);
// out: 34954

or send multiple parameters like this // 使用类似前端的写法

Get.toNamed("/profile/34954?flag=true&country=italy");

or

var parameters = <String, String>{"flag": "true","country": "italy",};
Get.toNamed("/profile/34954", parameters: parameters);

On second screen take the data by parameters as usually

print(Get.parameters['user']);
print(Get.parameters['flag']);
print(Get.parameters['country']);
// out: 34954 true italy

And now, all you need to do is use Get.toNamed() to navigate your named routes, without any context (you can call your routes directly from your BLoC or Controller class), and when your app is compiled to the web, your routes will appear in the url <3

Middleware

If you want to listen Get events to trigger actions, you can to use routingCallback to it // 拦截

GetMaterialApp(
  routingCallback: (routing) {
    if(routing.current == '/second'){
      openAds();
    }
  }
)

If you are not using GetMaterialApp, you can use the manual API to attach Middleware observer.

void main() {
  runApp(
    MaterialApp(
      onGenerateRoute: Router.generateRoute,
      initialRoute: "/",
      navigatorKey: Get.key,
      navigatorObservers: [
        GetObserver(MiddleWare.observer), // HERE !!!
      ],
    ),
  );
}

Create a MiddleWare class

class MiddleWare {
  static observer(Routing routing) {
    /// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
    ///If you need to enter any of these 3 events directly here,
    ///you must specify that the event is != Than you are trying to do.
    if (routing.current == '/second' && !routing.isSnackbar) {
      Get.snackbar("Hi", "You are on second route");
    } else if (routing.current =='/third'){
      print('last route called');
    }
  }
}

Now, use Get on your code:

class First extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            Get.snackbar("hi", "i am a modern snackbar");
          },
        ),
        title: Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Get.toNamed("/second");
          },
        ),
      ),
    );
  }
}

class Second extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            Get.snackbar("hi", "i am a modern snackbar");
          },
        ),
        title: Text('second Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Get.toNamed("/third");
          },
        ),
      ),
    );
  }
}

class Third extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Third Route"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Get.back();
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

SnackBars

To have a simple SnackBar with Flutter, you must get the context of Scaffold , or you must use a GlobalKey attached to your Scaffold

final snackBar = SnackBar(
  content: Text('Hi!'),
  action: SnackBarAction(
    label: 'I am a old and ugly snackbar :(',
    onPressed: (){}
  ),
);
// Find the Scaffold in the widget tree and use
// it to show a SnackBar.
Scaffold.of(context).showSnackBar(snackBar);

With Get:

Get.snackbar('Hi', 'i am a modern snackbar');

With Get, all you have to do is call your Get.snackbar from anywhere in your code or customize it however you want!

Get.snackbar(
  "Hey i'm a Get SnackBar!", // title
  "It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
  icon: Icon(Icons.alarm),
  shouldIconPulse: true,
  onTap:(){},
  barBlur: 20,
  isDismissible: true,
  duration: Duration(seconds: 3),
);


  ////////// ALL FEATURES //////////
  //     Color colorText,
  //     Duration duration,
  //     SnackPosition snackPosition,
  //     Widget titleText,
  //     Widget messageText,
  //     bool instantInit,
  //     Widget icon,
  //     bool shouldIconPulse,
  //     double maxWidth,
  //     EdgeInsets margin,
  //     EdgeInsets padding,
  //     double borderRadius,
  //     Color borderColor,
  //     double borderWidth,
  //     Color backgroundColor,
  //     Color leftBarIndicatorColor,
  //     List<BoxShadow> boxShadows,
  //     Gradient backgroundGradient,
  //     TextButton mainButton,
  //     OnTap onTap,
  //     bool isDismissible,
  //     bool showProgressIndicator,
  //     AnimationController progressIndicatorController,
  //     Color progressIndicatorBackgroundColor,
  //     Animation<Color> progressIndicatorValueColor,
  //     SnackStyle snackStyle,
  //     Curve forwardAnimationCurve,
  //     Curve reverseAnimationCurve,
  //     Duration animationDuration,
  //     double barBlur,
  //     double overlayBlur,
  //     Color overlayColor,
  //     Form userInputForm
  ///////////////////////////////////

If you prefer the traditional snackbar, or want to customize it from scratch, including adding just one line (Get.snackbar makes use of a mandatory (强制性) title and message), you can use Get.rawSnackbar(); which provides the RAW API on which Get.snackbar was built.

Dialogs

To open dialog:

Get.dialog(YourDialogWidget());

To open default dialog:

Get.defaultDialog(
  onConfirm: () => print("Ok"),
  middleText: "Dialog made in 3 lines of code"
);

You can also use Get.generalDialog instead of showGeneralDialog.

For all other Flutter dialog widgets, including cupertinos, you can use Get.overlayContext instead of context, and open it anywhere in your code. For widgets that don't use Overlay, you can use Get.context. These two contexts will work in 99% of cases to replace the context of your UI, except for cases where inheritedWidget is used without a navigation context.

BottomSheets

Get.bottomSheet is like showModalBottomSheet, but don't need of context.

Get.bottomSheet(
  Container(
    child: Wrap(
      children: <Widget>[
        ListTile(
          leading: Icon(Icons.music_note),
          title: Text('Music'),
          onTap: () {}
        ),
        ListTile(
          leading: Icon(Icons.videocam),
          title: Text('Video'),
          onTap: () {},
        ),
      ],
    ),
  )
);

Nested Navigation

Get made Flutter's nested navigation even easier. You don't need the context, and you will find your navigation stack by Id.

  • NOTE: Creating parallel navigation stacks can be dangerous. The ideal is not to use NestedNavigators, or to use sparingly (节制) . If your project requires it, go ahead, but keep in mind that keeping multiple navigation stacks in memory may not be a good idea for RAM consumption.

See how simple it is:

Navigator(
  key: Get.nestedKey(1), // create a key by index
  initialRoute: '/',
  onGenerateRoute: (settings) {
    if (settings.name == '/') {
      return GetPageRoute(
        page: () => Scaffold(
          appBar: AppBar(
            title: Text("Main"),
          ),
          body: Center(
            child: TextButton(
              color: Colors.blue,
              onPressed: () {
                Get.toNamed('/second', id:1); // navigate by your nested route by index
              },
              child: Text("Go to second"),
            ),
          ),
        ),
      );
    } else if (settings.name == '/second') {
      return GetPageRoute(
        page: () => Center(
          child: Scaffold(
            appBar: AppBar(
              title: Text("Main"),
            ),
            body: Center(
              child:  Text("second")
            ),
          ),
        ),
      );
    }
  }
),