colveor_ui
A Flutter UI foundation for Colveor apps. It wires up theming (light/dark),
routing (go_router), state (flutter_riverpod) and responsive sizing
(flutter_screenutil_plus), and ships a set of themed Vo* widgets.
Full documentation: colveor.skyapplabs.com/docs/flutter/overview. The source lives in docs/ — see Documentation.
Features
-
App shell:
ColveorAppsets upProviderScope, your config and routes, and responsive sizing (with a configurable design size) in one widget. -
Theming: extend
VoThemewith your own colors, sizes, typography and component styles to get a full Material 3ThemeDatafor light and dark mode. The selected theme mode is saved between launches. -
Routing: a
GoRouterbuilt from your route list andVoRoutingConfig. -
Screen helpers: theme, sizes, screen, navigation and toast helpers via
context.voin any widget, or directly as members of aVoState/VoConsumerState. -
Widgets:
Library Contents widgets/buttons.dartVoButton(primary, secondary, outline, text, danger, loading),VoIconButtonwidgets/inputs.dartText, password, search and select fields, phone input with country picker, PIN input, custom keyboard, form control widgets/display.dartVoText(with tappable[links]), avatar, counter badge/text, country flag, dividerwidgets/feedback.dartVoToast(success, error, warning, info, banner), feedback view, countdown actionwidgets/layout.dartVoScaffold,VoAppBar, scrollable form, tileswidgets/overlay.dartBottom sheets, dialogs, searchable selectable lists widgets/page_flow.dartMulti-step page flows with animated transitions widgets/dashboard.dartDashboard shell with drawer, end drawer and dock navigation
Installation
flutter pub add colveor_ui
or add it to pubspec.yaml yourself:
dependencies:
colveor_ui: ^0.3.0
It requires Dart SDK ^3.12.2.
Usage
1. Configure the app
import 'package:colveor_ui/config.dart';
class AppConfig extends VoConfig {
@override
String get appName => 'My App';
@override
VoRoutingConfig get routing => const VoRoutingConfig(initialLocation: '/');
}
VoRoutingConfig also takes the router settings apps usually adjust. The
redirect callback gets a Ref, so it can read your providers, and onInit
re-runs it when they change:
VoRoutingConfig(
initialLocation: '/splash',
onInit: (ref, notifier) =>
ref.listen(authProvider, (_, _) => notifier.notify()),
redirect: (ref, context, state) {
final signedIn = ref.read(authProvider).isSignedIn;
final atLogin = state.matchedLocation == '/login';
if (!signedIn) return atLogin ? null : '/login';
return atLogin ? '/home' : null;
},
observers: [MyAnalyticsObserver()],
errorBuilder: (context, state) => const NotFoundScreen(),
redirectLimit: 5,
debugLogDiagnostics: true,
)
To navigate outside the widget tree, use ref.read(voRouterProvider), or
voNavigatorKey for the root navigator and overlay. See
docs/routing.md.
VoConfig also has environment, initialCountryCode, favoriteCountries
and feedback, all with defaults you can override. Set the environment at
build time with --dart-define=FLAVOR=production.
2. Define the theme
Extend VoTheme. Every part has a default (DefaultColors, DefaultSizes,
DefaultTypography, DefaultComponentTheme), so class AppTheme extends VoTheme {} already works. Override only what you want to brand:
import 'package:colveor_ui/theme.dart';
import 'package:flutter/material.dart';
class AppTheme extends VoTheme {
@override
VoColors get colors => const AppColors();
@override
VoTypography get typography => const AppTypography();
}
class AppColors extends VoColors {
const AppColors({super.brightness});
@override
AppColors copyWith({Brightness? brightness}) =>
AppColors(brightness: brightness ?? this.brightness);
@override
VoColorMode get primaryMode =>
const VoColorMode(light: Color(0xFF4F46E5), dark: Color(0xFF818CF8));
}
class AppTypography extends VoTypography {
const AppTypography({super.colors, super.sizes});
@override
String get bodyFontFamily => 'Inter';
@override
AppTypography copyWith({VoColors? colors, VoSizes? sizes}) =>
AppTypography(colors: colors, sizes: sizes);
}
VoTheme.build passes the resolved brightness, colors and sizes to each
part's copyWith, so every part must implement copyWith. To change the
Material sub-themes (buttons, inputs, app bar, …), extend VoThemeData and
return it from createThemeData.
3. Define routes
import 'package:colveor_ui/colveor_ui.dart';
List<RouteBase> appRoutes(VoConfig config) => [
GoRoute(path: '/', builder: (_, _) => const HomeScreen()),
];
4. Run the app
import 'package:colveor_ui/colveor_ui.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final config = AppConfig();
await VoBootstrap.initialize(config: config);
runApp(
ColveorApp(
config: config,
appRoutes: appRoutes,
builder: (context, colveor) => MaterialApp.router(
theme: AppTheme().light,
darkTheme: AppTheme().dark,
themeMode: colveor.themeMode,
routerConfig: colveor.router,
),
),
);
}
Call colveor.toggleThemeMode() or colveor.setThemeMode(...) to change the
theme. Inside a ConsumerWidget, use ref.toggleThemeMode() or
ref.setThemeMode(...) instead, and read the selected mode with
ref.watch(voThemeControllerProvider). The mode is saved with VoStorage.
Screen sizing
ColveorApp initializes flutter_screenutil_plus so sizes scale with the
screen. Sizes scale against a 360×690 design by default. If your designs use
a different frame, pass its size:
ColveorApp(
config: config,
appRoutes: appRoutes,
designSize: const Size(390, 844),
minTextAdapt: true, // default
splitScreenMode: true, // default
builder: (context, colveor) => ...,
)
Startup work
VoBootstrap.initialize does nothing by default. The country list used by
VoPhoneInput and VoSelectCountryField (about 50 KB) loads the first time
one of them is built, so apps that don't use them never load it. To have it
ready before the first frame instead, preload it:
await VoBootstrap.initialize(config: config, preloadCountries: true);
Building screens
In any widget, context.vo gives you the theme, screen, navigation and toast
helpers for that context:
import 'package:colveor_ui/colveor_ui.dart';
import 'package:colveor_ui/widgets/buttons.dart';
import 'package:colveor_ui/widgets/layout.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) => VoScaffold(
appBar: AppBar(title: const Text('Home')),
body: Column(
spacing: context.vo.sizes.md,
children: [
Text('Welcome', style: context.vo.typography.titleLarge),
VoButton(
label: 'Save',
onPressed: () => context.vo.showSuccessToast('Saved'),
),
],
),
);
}
In a State, extend VoState (or VoConsumerState with Riverpod) to use the
same helpers as members:
class _ProfileScreenState extends VoState<ProfileScreen> {
@override
Widget build(BuildContext context) => VoButton(
label: 'Edit',
onPressed: () => push('/profile/edit'),
);
}
Helpers always resolve against the context of the element that is building or
being tapped, so they're safe in callbacks, dialogs and nested navigators.
There is no stateless base class: a stateless widget instance (a const one
in particular) can be mounted in several places at once, so it has no single
context of its own.
Libraries
| Import | Contents |
|---|---|
package:colveor_ui/colveor_ui.dart |
ColveorApp, VoBootstrap, ColveorRef, VoConfig, voConfigProvider, context.vo, ref.setThemeMode / ref.toggleThemeMode, VoState / VoConsumerState, everything in routing.dart |
package:colveor_ui/config.dart |
VoConfig, voConfigProvider, Environment, VoFeedbackConfig, VoRoutingConfig |
package:colveor_ui/routing.dart |
VoRoutingConfig and its callback types, voRouterProvider, voNavigatorKey, VoRouterNotifier, VoRouteObserver, plus GoRoute, ShellRoute, StatefulShellRoute, GoRouterState and context.go / push / pop from go_router |
package:colveor_ui/theme.dart |
VoTheme, colors, sizes, typography, component themes, voThemeControllerProvider |
package:colveor_ui/extensions.dart |
Theme extensions (context.colors, context.sizes, context.typography, …), context.vo, ref.setThemeMode, Color.darken / lighten |
package:colveor_ui/storage.dart |
VoStorage |
package:colveor_ui/utils.dart |
VoLogger, VoValidators, date / number / phone / text formatters |
package:colveor_ui/widgets/*.dart |
Widget groups (see Features) |
Documentation
| Page | Contents |
|---|---|
| Overview | Features and import paths |
| ColveorApp | ColveorApp, ColveorRef, VoBootstrap, testing |
| Configuration | VoConfig, voConfigProvider, Environment, feedback |
| Routing | VoRoutingConfig, redirect guards, voRouterProvider, navigation |
| Theming | VoTheme, tokens, component themes, theme mode |
| Base Screens | VoState, VoConsumerState and their helpers |
| Extensions | context.vo, context.colors and friends, ref.setThemeMode, Color.darken |
| Widgets | Every Vo* widget |
| Storage | VoStorage |
| Utilities | Logger, validators, formatters |
Example
example/ is a complete app: a routing guard backed by a session
saved with VoStorage, a phone and PIN sign-in flow, a dashboard with dock and
drawers, a custom theme with a light/dark toggle, and one showcase screen per
widget group. See example/README.md for where each part
lives.
cd example
flutter run
Development
Riverpod providers, Freezed models and JSON serializers are generated. After changing an annotated file, run:
dart run build_runner build --delete-conflicting-outputs