dust_flutter 0.1.4 copy "dust_flutter: ^0.1.4" to clipboard
dust_flutter: ^0.1.4 copied to clipboard

Flutter annotations and runtime APIs for Dust-generated routing, state management, and i18n.

example/dust_flutter_example.dart

import 'package:dust_flutter/i18n.dart';
import 'package:dust_flutter/route.dart';
import 'package:dust_flutter/state.dart';
import 'package:flutter/widgets.dart';

/// State type used by a generated ViewModel.
final class CounterState {
  /// Creates counter state.
  const CounterState({this.count = 0});

  /// Current counter value.
  final int count;
}

/// Source class annotated for Dust state generation.
///
/// In an app, add `part 'counter_view_model.g.dart';`, run `dust build`, and
/// extend the generated `$CounterViewModel` base shown in the state guide.
@ViewModel(state: CounterState)
final class CounterViewModelSource {
  /// Creates the annotated source class.
  const CounterViewModelSource();
}

/// A page annotated for typed route generation.
@AppRoute('/products/:id', name: 'product')
final class ProductPage extends StatelessWidget {
  /// Creates a product detail page.
  const ProductPage({required this.id, this.tab, super.key});

  /// Product id parsed from the path segment.
  final int id;

  /// Optional query parameter.
  final String? tab;

  @override
  Widget build(BuildContext context) {
    return Text('Product $id ${tab ?? ''}');
  }
}

/// Search route demonstrating nullable and default-valued query parameters.
@AppRoute('/products', name: 'productSearch', guards: [])
final class ProductSearchPage extends StatelessWidget {
  /// Creates a product search page.
  const ProductSearchPage({
    this.query,
    this.page = 1,
    this.showArchived = false,
    super.key,
  });

  /// Search text encoded as a nullable query parameter.
  final String? query;

  /// Current page encoded as a default-valued query parameter.
  final int page;

  /// Whether archived products are shown.
  final bool showArchived;

  @override
  Widget build(BuildContext context) {
    return Text('Search ${query ?? ''} page $page archived $showArchived');
  }
}

/// Fullscreen picker route that returns a selected product id.
@AppRoute(
  '/product-picker',
  name: 'productPicker',
  result: int,
  guards: [],
  transition: BottomToTopPageTransitionsBuilder(),
  fullscreenDialog: true,
)
final class ProductPickerPage extends StatelessWidget {
  /// Creates the demo product picker page.
  const ProductPickerPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Pick product');
  }
}

/// Public invite route for signed-out magic links.
@AppRoute('/invite/:code', name: 'invite', guards: [])
final class InvitePage extends StatelessWidget {
  /// Creates an invite page.
  const InvitePage({required this.code, this.team, super.key});

  /// Invite token from the URL path.
  final String code;

  /// Optional team hint from the query string.
  final String? team;

  @override
  Widget build(BuildContext context) {
    return Text('Invite $code ${team ?? ''}');
  }
}

/// Organization-scoped detail route with path and query parameters.
@AppRoute('/orgs/:orgId/projects/:projectId', name: 'orgProject')
final class OrgProjectPage extends StatelessWidget {
  /// Creates an organization project page.
  const OrgProjectPage({
    required this.orgId,
    required this.projectId,
    this.tab,
    super.key,
  });

  /// Organization or workspace id from the path.
  final String orgId;

  /// Project id from the path.
  final int projectId;

  /// Optional selected tab from the query string.
  final String? tab;

  @override
  Widget build(BuildContext context) {
    return Text('Org $orgId project $projectId ${tab ?? ''}');
  }
}

/// Shell used by setup flow routes.
class SetupShell extends StatelessWidget {
  /// Creates a setup shell.
  const SetupShell({required this.child, super.key});

  /// Flow step rendered inside the setup shell.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Setup'),
        Expanded(child: child),
      ],
    );
  }
}

/// Shareable entry point for a multi-step setup flow.
@AppRoute('/setup', name: 'setup', guards: [], shell: SetupShell)
final class SetupPage extends StatelessWidget {
  /// Creates the setup start page.
  const SetupPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Find device');
  }
}

/// Second setup step that inherits [SetupShell].
@AppRoute('/setup/connect', name: 'setupConnect')
final class SetupConnectPage extends StatelessWidget {
  /// Creates the setup connect page.
  const SetupConnectPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Connect');
  }
}

/// Shared shell widget used by the shell route examples below.
class DemoShell extends StatelessWidget {
  /// Creates a demo shell.
  const DemoShell({required this.child, super.key});

  /// Page rendered inside the shell body.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Demo shell'),
        Expanded(child: child),
      ],
    );
  }
}

/// Home route wrapped by [DemoShell].
@AppRoute('/', name: 'home', shell: DemoShell, guards: [])
final class HomePage extends StatelessWidget {
  /// Creates the demo home page.
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Home');
  }
}

/// Settings route inherits [DemoShell] from the root shell route.
@AppRoute('/settings', name: 'settings', guards: [])
final class SettingsPage extends StatelessWidget {
  /// Creates the demo settings page.
  const SettingsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Settings');
  }
}

/// Child route that also inherits [DemoShell] from [HomePage].
@AppRoute('/orders', name: 'orders')
final class OrdersPage extends StatelessWidget {
  /// Creates the demo orders page.
  const OrdersPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Orders');
  }
}

/// Tab root route that owns the home branch stack.
@AppRoute('/tabs/home', name: 'tabHome', shell: DemoShell, branch: 'mainTabs')
final class TabHomePage extends StatelessWidget {
  /// Creates the demo home tab page.
  const TabHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Home tab');
  }
}

/// Child page that inherits the home tab branch.
@AppRoute('/tabs/home/details', name: 'tabHomeDetails')
final class TabHomeDetailsPage extends StatelessWidget {
  /// Creates the demo home tab detail page.
  const TabHomeDetailsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Home tab details');
  }
}

/// Tab root route that owns a separate orders branch stack.
@AppRoute(
  '/tabs/orders',
  name: 'tabOrders',
  shell: DemoShell,
  branch: 'ordersTabs',
)
final class TabOrdersPage extends StatelessWidget {
  /// Creates the demo orders tab page.
  const TabOrdersPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Orders tab');
  }
}

/// Shell used when a child subtree needs a more specific layout.
class ReportsShell extends StatelessWidget {
  /// Creates a reports shell.
  const ReportsShell({required this.child, super.key});

  /// Page rendered inside the reports shell.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Reports shell'),
        Expanded(child: child),
      ],
    );
  }
}

/// Child route overriding the inherited shell with [ReportsShell].
@AppRoute('/reports', name: 'reports', shell: ReportsShell)
final class ReportsPage extends StatelessWidget {
  /// Creates the demo reports page.
  const ReportsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Reports');
  }
}

/// Renders an async lifecycle state produced by generated async ViewModels.
Widget renderProducts(AsyncState<List<String>> state) {
  return switch (state) {
    AsyncInitial<List<String>>() ||
    AsyncLoading<List<String>>() =>
      const Text('Loading'),
    AsyncData<List<String>>(:final data) => Text('Loaded ${data.length}'),
    AsyncFailure<List<String>>(:final error, :final previousData) => Text(
        'Error: $error. Previous count: ${previousData?.length ?? 0}',
      ),
  };
}

/// Creates an in-memory i18n controller for tests or demos.
I18nController createDemoI18n() {
  return I18nController(
    config: const I18nConfig(locales: ['en'], fallbackLocale: 'en'),
    bundles: const [
      I18nBundle(
        locale: 'en',
        namespace: 'home',
        messages: {'title': 'Home'},
      ),
    ],
  );
}
1
likes
160
points
416
downloads

Documentation

Documentation
API reference

Publisher

verified publisherwidget-lab.dev

Weekly Downloads

Flutter annotations and runtime APIs for Dust-generated routing, state management, and i18n.

Repository (GitHub)
View/report issues
Contributing

Topics

#codegen #flutter #routing #state-management #localization

License

MIT (license)

Dependencies

flutter

More

Packages that depend on dust_flutter