fin_ui 0.1.1 copy "fin_ui: ^0.1.1" to clipboard
fin_ui: ^0.1.1 copied to clipboard

A reusable Flutter UI foundation with adaptive navigation, theme tokens, split panes, and polished app controls.

FinUI #

FinUI is a reusable Flutter UI foundation for small and medium app clients. It provides theme tokens, app-level controls, adaptive navigation, split-pane layouts, and a compact page transition policy extracted from GXNU Class Tool.

The package is intentionally conservative: it uses Flutter ThemeData, MaterialPageRoute, IndexedStack, and small custom controls instead of large Material navigation components. The goal is to keep app pages fast, stable, and visually consistent across mobile and desktop.

Features #

  • Theme system: FuiTheme.light(), FuiTheme.dark(), FuiAccent, dynamic system accent support, palette extension, typography defaults, scroll behavior, and page transition policy.
  • Design tokens: spacing, radius, icon sizes, animation durations, page/card/tile insets.
  • Controls: buttons, icon buttons, text fields, combo boxes, tags, tiles, sections, surfaces, progress bars, refresh indicators, empty states, bottom tabs, navigation rail, and toast.
  • Adaptive navigation: FuiNavigationShell switches between bottom tabs, navigation rail, and split layout from screen width.
  • Detail navigation: FuiNavigation.openDetail() keeps one API for compact full-page pushes, bottom sheets, dialogs, and wide-screen secondary panes.
  • Split layout: FuiAdaptiveSplitScaffold and FuiSplitNavigationScaffold cover common master-detail and optional supplementary-pane screens.
  • Original route feel: FuiPageRoute preserves the old Class Tool route semantics by inheriting from MaterialPageRoute and delegating transitions back to super.buildTransitions().

Installation #

Use the package from pub.dev after it is published:

dependencies:
  fin_ui: ^0.1.1

For local development before publishing:

dependencies:
  fin_ui:
    path: ../fin_ui

Import the package entrypoint:

import 'package:fin_ui/fin_ui.dart';

package:fin_ui/fui.dart is kept as a compatibility alias and exports the same public API.

Quick start #

Wrap your app with FinUI themes and scroll behavior:

import 'package:fin_ui/fin_ui.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const ExampleApp());
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FinUI Example',
      theme: FuiTheme.light(),
      darkTheme: FuiTheme.dark(),
      scrollBehavior: FuiTheme.scrollBehavior,
      home: const ExampleShell(),
    );
  }
}

Use FuiNavigationShell as the app-level shell:

class ExampleShell extends StatefulWidget {
  const ExampleShell({super.key});

  @override
  State<ExampleShell> createState() => _ExampleShellState();
}

class _ExampleShellState extends State<ExampleShell> {
  int selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return FuiNavigationShell(
      selectedIndex: selectedIndex,
      onSelected: (index) => setState(() => selectedIndex = index),
      autoExtendRail: true,
      splitPlan: const FuiSplitNavigationPlan(
        placeholderData: FuiSecondaryPlaceholderData(
          icon: FUIIcons.apps,
          title: 'Select a page',
          subtitle: 'Details will appear here on wide screens.',
        ),
      ),
      items: const [
        FuiNavigationItem(
          destination: FuiNavigationDestination(
            icon: FUIIcons.home,
            selectedIcon: FUIIcons.homeFilled,
            label: 'Home',
          ),
          page: HomePage(),
        ),
        FuiNavigationItem(
          destination: FuiNavigationDestination(
            icon: FUIIcons.calendar,
            selectedIcon: FUIIcons.calendarFilled,
            label: 'Schedule',
          ),
          page: SchedulePage(),
        ),
        FuiNavigationItem(
          destination: FuiNavigationDestination(
            icon: FUIIcons.settings,
            selectedIcon: FUIIcons.settingsFilled,
            label: 'Settings',
          ),
          page: SettingsPage(),
        ),
      ],
    );
  }
}

Theme and tokens #

FuiTheme registers FUIPalette as a ThemeExtension, so controls can read semantic colors from the build context:

final colors = context.colors;

Use FUITokens and FUIInsets instead of hard-coded spacing for app UI:

Padding(
  padding: FUIInsets.page,
  child: FUISurface(
    child: Column(
      children: const [
        FUITag(label: 'Ready', variant: FUITagVariant.success),
        SizedBox(height: FUITokens.gap12),
        FUIButton(label: 'Continue'),
      ],
    ),
  ),
);

Dynamic system accent colors are available through FuiDynamicThemeBuilder:

FuiDynamicThemeBuilder(
  builder: (context, lightTheme, darkTheme) {
    return MaterialApp(
      theme: lightTheme,
      darkTheme: darkTheme,
      scrollBehavior: FuiTheme.scrollBehavior,
      home: const ExampleShell(),
    );
  },
);

FinUI navigation has two layers.

The app shell layer uses FuiNavigationShell. On compact widths it renders the custom bottom tab bar. On wider layouts it renders the custom rail. On large landscape layouts it can use split panes.

The detail layer uses FuiNavigation.openDetail():

FUITile(
  title: 'Open detail',
  subtitle: 'Compact screens push a page; split screens use the right pane.',
  onTap: () {
    FuiNavigation.openDetail(
      context,
      builder: (context) => const DetailPage(),
    );
  },
);

Do not replace this with ad-hoc PageRouteBuilder animations. FuiNavigation.openDetail() uses FuiPageRoute for compact page pushes and uses the secondary pane navigator in split mode. This keeps navigation behavior consistent across apps.

Breakpoints #

Default navigation breakpoints are:

Layout Default rule
Bottom tabs width < 720
Navigation rail width >= 720
Split layout width >= 900, height >= 600, aspect ratio >= 1.2
Extended rail enabled by autoExtendRail, width >= 1100

Override them when an app needs different behavior:

const breakpoints = FuiNavigationBreakpoints(
  railWidth: 760,
  extendedRailWidth: 1180,
  splitWidth: 980,
);

Split-pane pages #

Use FuiAdaptiveSplitScaffold for list-detail pages:

FuiAdaptiveSplitScaffold<String>(
  masterTitle: 'Items',
  items: const [
    FuiSplitItem(value: 'a', title: 'Item A', subtitle: 'Primary detail'),
    FuiSplitItem(value: 'b', title: 'Item B', subtitle: 'Secondary detail'),
  ],
  selectedValue: selectedId,
  onSelected: (value) => setState(() => selectedId = value),
  plan: const FuiSplitNavigationPlan(
    placeholderData: FuiSecondaryPlaceholderData(
      title: 'Select an item',
      subtitle: 'The item detail appears here.',
    ),
  ),
  detailBuilder: (context, id) => DetailPane(id: id),
  supplementaryBuilder: (context, id) => InspectorPane(id: id),
);

When a detail page uses FuiPageHead, FinUI automatically shows a close button for the first right-pane detail and a back button for deeper pages in the local detail stack.

Controls #

Common controls exported by the package:

Area API
Layout FUIPage, FUISurface, FUISection, FUITile
Input FUITextField, FUIComboBox, FUISegmentedControl, FUISelectionTile, FUISlider
Actions FUIButton, FUIIconButton, FUIFloatingActionButton
Feedback FUIToast, FUIProgressBar, FUIRefresh, FUIEmptyState, FUITag
Navigation FUIAppBar, FUIButtonGroupTabBar, FUIBottomTabBar, FUINavRail, FuiNavigationShell, FuiNavigation.openDetail()
Overlays FUIDialog, FUIToast

Toast can be shown with an explicit context:

FUIToast.success(context, message: 'Saved');

Global toast helpers use GetX context lookup and require the host app to provide a GetX overlay context, for example by using GetMaterialApp:

FUIToast.successGlobal(message: 'Saved');

Prefer context-based toast in package examples and reusable widgets.

Route transition policy #

The original Class Tool used normal MaterialPageRoute pushes and controlled the animation through ThemeData.pageTransitionsTheme. FinUI preserves that behavior.

Current policy:

  • Android, Windows, Linux, Fuchsia: PredictiveBackPageTransitionsBuilder.
  • iOS, macOS: CupertinoPageTransitionsBuilder.
  • Compact detail pushes: FuiPageRoute, which extends MaterialPageRoute.
  • FuiPageRoute.buildTransitions() forces FuiTheme.pageTransitionsTheme, then delegates to super.buildTransitions().

Do not hand-write route animations unless a component has a separate, documented reason. In particular, do not replace FuiPageRoute with PageRouteBuilder plus guessed SlideTransition/FadeTransition timings.

Repository workflow #

Local checks:

flutter pub get
dart format --set-exit-if-changed lib test example
flutter analyze
flutter test
flutter pub publish --dry-run

The GitHub Actions workflow runs the same checks on pull requests and pushes.

Publishing #

Before publishing:

  1. Update version in pubspec.yaml.
  2. Update CHANGELOG.md.
  3. Run the local checks above.
  4. Verify flutter pub publish --dry-run output and file list.
  5. Publish with flutter pub publish.

Keep public APIs stable after 1.0.0. Before that, prefer 0.x.y releases or prerelease versions such as 0.2.0-dev.1 while the design system is still moving.

License #

FinUI is licensed under the MIT License. See LICENSE.

1
likes
150
points
168
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A reusable Flutter UI foundation with adaptive navigation, theme tokens, split panes, and polished app controls.

Repository (GitHub)
View/report issues
Contributing

Topics

#flutter #ui #design-system #adaptive #navigation

License

MIT (license)

Dependencies

dynamic_color, easy_refresh, fluentui_system_icons, flutter, get

More

Packages that depend on fin_ui