This library provides unified cross-platform components for Flutter apps.
Instead of writing platform-conditional code (if (Platform.isIOS) ...) on every screen, nanna_flutter_platform exposes generic widgets (like NaButton) that automatically translate into the appropriate native design system:
- Material Design for Android, Web, and Linux
- Cupertino for iOS and macOS
Visual Comparison
Below are side-by-side examples of the exact same code rendering automatically in Material Design (Android) and Cupertino (iOS).
| Android (Material) | iPhone (Cupertino) |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Supported Widgets Status
| Flutter Widget (Material) | Cupertino Equivalent | Implemented | Component Name |
|---|---|---|---|
MaterialApp |
CupertinoApp |
✅ | NaApp |
Scaffold |
CupertinoPageScaffold |
✅ | NaScaffold |
AppBar |
CupertinoNavigationBar |
✅ | NaAppBar |
ElevatedButton |
CupertinoButton |
✅ | NaButton |
IconButton |
CupertinoButton (icon) |
✅ | NaIconButton |
Switch |
CupertinoSwitch |
✅ | NaSwitch |
Checkbox |
CupertinoCheckbox |
✅ | NaCheckbox |
Slider |
CupertinoSlider |
✅ | NaSlider |
CircularProgressIndicator |
CupertinoActivityIndicator |
✅ | NaProgressIndicator |
Card |
Container (decorated) |
✅ | NaCard |
AlertDialog |
CupertinoAlertDialog |
✅ | NaAlertDialog |
BottomNavigationBar |
CupertinoTabBar |
✅ | NaBottomNavigationBar |
DatePicker |
CupertinoDatePicker |
✅ | NaDatePicker |
TimePicker |
CupertinoTimerPicker |
✅ | NaTimePicker |
ListTile |
CupertinoListTile |
✅ | NaListTile |
Radio |
CupertinoRadio |
✅ | NaRadio |
Icon |
Icon (cupertino variants) |
✅ | NaIcon |
Dialog Action |
CupertinoDialogAction |
✅ | NaDialogAction |
TextField |
CupertinoTextField |
✅ | NaTextField |
PageRoute |
CupertinoPageRoute |
✅ | NaPageRoute |
TabBar |
CupertinoTabBar |
⏳ | NaTabBar |
TabBarView |
CupertinoTabView |
⏳ | NaTabView |
DropdownButton |
CupertinoPicker |
⏳ | NaDropdown |
RefreshIndicator |
CupertinoSliverRefreshControl |
⏳ | NaRefreshIndicator |
BottomSheet |
CupertinoActionSheet |
⏳ | NaActionSheet |
Scrollbar |
CupertinoScrollbar |
⏳ | NaScrollbar |
SearchBar |
CupertinoSearchTextField |
⏳ | NaSearchBar |
How to use the library
1. Initialize the Scope (NaUiTypeScope)
To make your entire app (or a portion of it) use a specific design system, wrap your widgets inside a NaUiTypeScope.
You can pass a fallback chain of UI types. The widgets will attempt to render themselves according to the first supported type in the list, falling back gracefully to the next ones if a custom style is not supported.
import 'package:flutter/widgets.dart';
import 'package:nanna_flutter_platform/nanna_flutter_platform.dart';
void main() {
runApp(
NaUiTypeScope(
// Priority list of UI types (e.g., custom style first, then cupertino, falling back to material)
uiTypes: [NaUiType('custom'), NaUiType.cupertino],
child: const MyApp(),
)
);
}
2. Create and use Widgets (Options Builder Pattern)
When using NaPlatform widgets, you can pass common parameters (like child, onPressed) and define platform-specific options at runtime using the optionsBuilder function.
This pattern (based on Marker Interfaces) guarantees total Type Safety and perfect decoupling.
NaButton(
onPressed: () => print('Pressed!'),
optionsBuilder: (BuildContext context, NaUiType uiType) {
// Specific options for Material
if (uiType == NaUiType.material) {
return NaButtonOptionsMaterial(
autofocus: true,
clipBehavior: Clip.hardEdge,
);
}
// Options for third-party plugins (e.g. macos_ui)
// if (uiType == macosUiType) {
// return MacosButtonOptions(useAcrylicEffect: true);
// }
return null;
},
child: const Text('Submit'),
)
This ensures that the widget remains clean and can accommodate configurations for UI plugins added in the future without needing to be modified.
3. Navigation (NaPageRoute)
When changing pages, standard Flutter routing pushes either a material transition or requires you to manually specify a CupertinoPageRoute. To get the correct native transition (like the swipe-back gesture on iOS) based on the active NaUiType, use NaPageRoute.create:
Navigator.push(
context,
NaPageRoute.create(
context,
builder: (context) => const MyNextPage(),
),
);
4. Extending with External Libraries (Plugins)
The library uses a Widget Builder Registry that allows external packages to add support for new design systems (like macos_ui) dynamically.
Read the full guide and example here: Extending the Platform with External Libraries
Technical Deep Dives
- Development Guidelines: Strict constraints, target platforms (iOS/Android), offline-first storage logic, dependencies (e.g.,
nanna_flutter), and Dart/Flutter coding styles.







