패키지, 저장소 및 앱 카테고리 검색

패키지 상세v0.0.9

octopus

상태와 중첩 탐색에 초점을 맞춘 플러터용 크로스플랫폼 선언형 라우터입니다. PlugFox가 ❤️로 제작했습니다.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
287
좋아요
98
Pub 점수
140

패키지 설치

flutter pub add octopus

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

Octopus는 상태 관리와 중첩 탐색을 강조하는 크로스플랫폼, 선언적 라우팅 솔루션입니다. 단순성과 확장성을 고려해 설계되었으며, 안드로이드, iOS, 웹, macOS, 윈도우, 리눅스에서 구조적이고 반응형으로 경로를 정의할 수 있게 해줍니다. PlugFox가 ❤️ 로 제작한 Octopus는 복잡한 네비게이션 흐름을 간소화하면서 앱 수명 주기 동안 깔끔한 코드와 일관된 상태 처리를 유지합니다.

사용 사례

  • 중첩 경로 탐색
  • 상태 유지 라우트 전환
  • 크로스 플랫폼 앱 라우팅
  • 복잡한 UI 흐름 관리
  • 반응형 경로 정의

주요 기능

  • 선언적 라우팅 구문
  • 내장 상태 보존
  • 중첩된 네비게이터 지원
  • 크로스 플랫폼 호환성
  • 타입 안전한 라우팅 처리
  • 기존 Flutter 앱과의 간편한 통합

적합한 대상

  • 복잡한 Flutter 앱을 개발하는 개발자
  • 강력한 탐색 패턴이 필요한 팀
  • 딥링크 및 상태 지속성이 필요한 앱
  • 유지보수 가능한 라우팅 로직을 우선시하는 프로젝트
  • Flutter 초보자가 고급 탐색을 배우는 중

고려 사항

  • Flutter의 네비게이터 개념을 이해해야 합니다
  • 간단한 앱에 부담을 줄 수 있습니다
  • 중첩 탐색 패턴의 학습 곡선
  • 성숙한 대안에 비해 문서가 제한적

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

Octopus: A Declarative Router for Flutter

Pub Actions Status Example Coverage License: MIT Linter GitHub stars

Octopus is a declarative router for Flutter. Its main concept and distinction from other solutions is dynamic navigation through state mutations. It is a TRULY DECLARATIVE router, where you don’t change the state imperatively using push and pop commands. Instead, you (or the user) specify the desired outcome through state mutations or the address bar, and the router delivers a predictably expected result.

Most solutions use templating for navigation, pre-describing all possible router states with hardcoding (and code generation). While this is an expected and predictable approach in traditional BE SSR (where the page is assembled server-side), it has several serious drawbacks on the client side:

  1. You cannot predict all possible states in advance.
  2. There is no ability to implement routes of arbitrary depth (e.g., /shop/category~id=1/category~id=12/category~id=123/product~id=1234).
  3. Understanding what happens in cases with nested routes can be quite complex.
  4. Loss of state in nested routes, as such routers typically display only the active route, even though the nested state continues to exist.

What does the current solution offer?

  1. Changing state through mutation.
  2. Nested navigation, both through the out-of-the-box solution and your custom implementation.
  3. A router state machine and case implementation based on state changes. For example, it’s very easy to implement breadcrumbs or integrate a tab/sidebar with router state arguments.
  4. A history of states, allowing you to implement a time machine or, after reauthentication, return the user to where they started. Or simply log this for analytics purposes.
  5. A user-friendly API with a "foolproof" design, where mutable states are clear and methods for changing them are provided, and immutable states are also clearly indicated.
  6. A strong focus on Guards. Since the user can now obtain any desired configuration, you might want to "clip their wings." Ensure that the "Home" page is always at the root except for logged-out users, change states during navigation or upon reaching certain conditions, for example, showing login for unauthorized users. Recheck all navigation states on a specific event, just pass the subscription to your guard.
  7. Implementation of dialogs through declarative navigation. No more showing dialogs from the navigator and mixing anonymous imperative dialogs with declarative navigation.
  8. Preserve and display the entire navigation tree in the URL and deep links, not just the active route!
  9. Clear debugging and the representation of the state as a string will simplify development.
  10. Concurrency support a out of the box.

With a declarative approach, the only limit is your imagination!


Installation

Add the following dependency to your pubspec.yaml file:

dependencies:
  octopus: <version>

Get Started

Set up your routes. You can use an enum, make a few sealed classes, or both. This doesn't matter. A recommended and simple way is to get started with enums. Override a builder function to link your nodes and widgets. Optionally, set up a "title" field for any route.

enum Routes with OctopusRoute {
  home('home', title: 'Home'),
  gallery('gallery', title: 'Gallery'),
  picture('picture', title: 'Picture'),
  settings('settings', title: 'Settings');

  const Routes(this.name, {this.title});

  @override
  final String name;

  @override
  final String? title;

  @override
  Widget builder(BuildContext context, OctopusState state, OctopusNode node) =>
      switch (this) {
        Routes.home => const HomeScreen(),
        Routes.gallery => const GalleryScreen(),
        Routes.picture => PictureScreen(id: node.arguments['id']),
        Routes.settingsDialog => const SettingsDialog(),
      };
}

Example

Create an Octopus router instance. During main initialization or state of the root App widget. To do so, pass a list of all possible routes. Optionally, set a defaultRoute as a route by default.

router = Octopus(
  routes: Routes.values,
  defaultRoute: Routes.home,
);

Example

Add configuration from Octopus.config to the WidgetApp.router constructor.

MaterialApp.router(
  routerConfig: router.config,
)

Example

How to navigate

Use the context.octopus.setState((state) => ...) method as a basic navigation method.

And realize any navigation logic inside the callback as you please.

context.octopus.setState((state) =>
  state
    ..findByName('catalog-tab')?.add(Routes.category.node(
      arguments: <String, String>{'id': category.id},
    )));

Of course, there are other ways to navigate, primarily shortcuts for the most common cases.

context.octopus.push(Routes.shop)

But you can truly do anything you want. Just change the state, children, nodes, and arguments as you please. Everything is in your hands and just works fine, that's a declarative approach as it should be.

Guards

Guards are a powerful tool for controlling navigation. They allow you to check the state of the router and mutate/cancel navigation if necessary. For example, you can check the user's authorization and redirect them to the login page if they are not authorized.

Examples:

  1. How to make an authentification guard and restore the previous state after login
  2. How to place a Home route at the root of the navigation stack

Glossary

  1. State - the overall state of the router can be mutable (while the user mutates the new desired state and in guards) or immutable (all other times). The state can include a hash table of arguments, which are global arguments of the current state. These can be used at your discretion.

  2. Node - the components that constitute the state form a tree structure in the case of nested navigation. Each node has a name and arguments (usually parameters passed to a screen, like an identifier). At each level, within each list of nodes, the combination of name and arguments must be unique, as this forms the unique key of the node.

  3. Route - router has a list of possible routes that can be used in the project. The router matches nodes and routes by their names. Routes contain information on how to construct a page for the navigator.

State structure

Let's take a look at the next nested tree which we want to get:

Home
Shop
├─Catalog-Tab
│ ├─Catalog
│ ├─Category {id: electronics}
│ ├─Category {id: smartphones}
│ └─Product {id: 3}
└─Basket-Tab
  ├─Basket
  └─Checkout

Also, we want the global argument shop with the value catalog to refer to a tab bar state.

Let's create the following state to represent our expectations:

final state = OctopusState(
  intention: OctopusStateIntention.auto,
  arguments: <String, String>{'shop': 'catalog'},
  children: <OctopusNode>[
    OctopusNode(
      name: 'home',
      arguments: <String, String>{},
      children: <OctopusNode>[],
    ),
    OctopusNode(
      name: 'shop',
      arguments: <String, String>{},
      children: <OctopusNode>[
        OctopusNode(
          name: 'catalog-tab',
          arguments: <String, String>{},
          children: <OctopusNode>[
            OctopusNode(
              name: 'catalog',
              arguments: <String, String>{},
              children: <OctopusNode>[],
            ),
            OctopusNode(
              name: 'category',
              arguments: <String, String>{'id': 'electronics'},
              children: <OctopusNode>[],
            ),
            OctopusNode(
              name: 'category',
              arguments: <String, String>{'id': 'smartphones'},
              children: <OctopusNode>[],
            ),
            OctopusNode(
              name: 'product',
              arguments: <String, String>{'id': '3'},
              children: <OctopusNode>[],
            ),
          ],
        ),
        OctopusNode(
          name: 'basket-tab',
          arguments: <String, String>{},
          children: <OctopusNode>[
            OctopusNode(
              name: 'basket',
              arguments: <String, String>{},
              children: <OctopusNode>[],
            ),
            OctopusNode(
              name: 'checkout',
              arguments: <String, String>{},
              children: <OctopusNode>[],
            ),
          ],
        ),
      ],
    ),
  ],
);

Take a look closer. That's a tree structure. Each component of that tree has a List<OctopusNode> children for children nodes and arguments for the current node. States have arguments, too; it's your global arguments. Each node also has a name; by this name, you can identify this node and link it with your routes table.

If we try to represent this state as a location string, we get something like that: /home/shop/.catalog-tab/..catalog/..category~id=electronics/..category~id=smartphones/..product~id=3/.basket-tab/..basket/..checkout?shop=catalog

Changelog

Refer to the Changelog to get all release notes.

Maintainers

Funding

If you want to support the development of our library, there are several ways you can do it:

We appreciate any form of support, whether it's a financial donation or just a star on GitHub. It helps us to continue developing and improving our library. Thank you for your support!

MIT License

카테고리

의존성

의존성 목록 3 개

소스 저장소

이 패키지를 사용하는 프로젝트

아직 데이터가 없습니다.