fl_core_kit 0.1.0 copy "fl_core_kit: ^0.1.0" to clipboard
fl_core_kit: ^0.1.0 copied to clipboard

A Flutter toolkit providing theme management, locale switching, extension methods, state mixins, system utilities, and common widgets.

fl_core_kit #

pub package Flutter License

A Flutter toolkit providing theme management, locale switching, extension methods, state mixins, system utilities, async timing utilities, and common widgets.


Features #

  • 🎨 Theme system β€” Light/dark theme switching with system-follow mode, customizable theme data base class
  • 🌍 Locale management β€” Runtime language switching with inherited widget propagation
  • 🧩 Extension methods β€” BuildContext, GlobalKey, List, Map, ScrollNotification, String
  • πŸ”§ State mixins β€” Safe setState, performance tracing via Timeline
  • ⏱ Async timing β€” Measure Future execution time with TimeMeasureResult
  • πŸ“± Common widgets β€” Decimal input formatter, keyboard avoidance, bottom navigation, layout change notifier, and more

Installation #

dependencies:
  fl_core_kit:
    git:
      url: https://github.com/LqDeveloper/fl_core_kit.git

Or use the local path:

dependencies:
  fl_core_kit:
    path: ../fl_core_kit

Import:

import 'package:fl_core_kit/fl_core_kit.dart';

Quick Start #

Widget nesting order (outer to inner): FlLocaleWidget β†’ FlThemeWidget β†’ MaterialApp.

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

// 1. Define custom theme data (extend FlBaseThemeData)
class AppTheme extends FlBaseThemeData {
  final Color primary;
  final Color background;

  const AppTheme({required this.primary, required this.background});

  factory AppTheme.light() => const AppTheme(
        primary: Colors.blue,
        background: Colors.white,
      );

  @override
  Color? get primaryColor => primary;
  @override
  Color? get scaffoldBgColor => background;
  @override
  ColorScheme? get colorScheme =>
      ColorScheme.fromSeed(seedColor: primary);
}

// 2. App entry
void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return FlLocaleWidget(
      initLocale: const Locale('en', 'US'),
      builder: (context, locale) {
        return FlThemeWidget<AppTheme>(
          initialModel: FlThemeMode.system,
          lightTheme: AppTheme.light(),
          darkTheme: AppTheme.dark(),
          builder: (context, themeData) {
            return MaterialApp(
              title: 'My App',
              theme: themeData.themeData,
              locale: locale,
              home: const HomePage(),
            );
          },
        );
      },
    );
  }
}

// 3. Use in pages
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final theme = context.wt<AppTheme>();
    final mode = context.themeMode;

    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          children: [
            Text('Primary: ${theme.primary}'),
            Text('Mode: $mode'),
            ElevatedButton(
              onPressed: () => context.currentLocale = const Locale('zh'),
              child: const Text('Switch to Chinese'),
            ),
            ElevatedButton(
              onPressed: () => context.setupTheme(FlThemeMode.dark),
              child: const Text('Dark Mode'),
            ),
          ],
        ),
      ),
    );
  }
}

Modules #

1. Theme System #

1.1 FlBaseThemeData β€” Theme Data Base Class

Abstract base class for custom themes. Override getters to define component styles.

class MyTheme extends FlBaseThemeData {
  final Color primary;
  final Color background;

  const MyTheme({required this.primary, required this.background});

  factory MyTheme.light() => const MyTheme(
        primary: Colors.blue,
        background: Colors.white,
      );

  @override
  Color? get primaryColor => primary;
  @override
  Color? get scaffoldBgColor => background;
  @override
  ColorScheme? get colorScheme =>
      ColorScheme.fromSeed(seedColor: primary);
  @override
  Color? get splashColor => Colors.transparent;
  @override
  Color? get highlightColor => Colors.transparent;
  @override
  InteractiveInkFeatureFactory? get splashFactory => NoSplash.splashFactory;
  @override
  AppBarTheme? get appBarTheme => null;
  @override
  TabBarThemeData? get tabBarTheme => null;
  @override
  BottomNavigationBarThemeData? get bottomNavigationBarTheme => null;
  @override
  BottomSheetThemeData? get bottomSheetTheme => null;
  @override
  DialogThemeData? get dialogTheme => null;
  @override
  SystemUiOverlayStyle? get systemOverlay => null;
  @override
  String? get fontFamily => null;
  @override
  String? get package => null;
  @override
  PageTransitionsTheme? get pageTransitionsTheme => null;

  /// All getters are automatically assembled into ThemeData.
  @override
  ThemeData get themeData => super.themeData;
}
Getter Type Description
primaryColor Color? Primary color
colorScheme ColorScheme? Color scheme
scaffoldBgColor Color? Scaffold background color
splashColor Color? Splash/ripple color (default transparent)
highlightColor Color? Highlight color (default transparent)
splashFactory InteractiveInkFeatureFactory? Splash factory (default NoSplash)
fontFamily String? Global font family
systemOverlay SystemUiOverlayStyle? Status bar / navigation bar style
appBarTheme AppBarTheme? AppBar theme
tabBarTheme TabBarThemeData? TabBar theme
bottomNavigationBarTheme BottomNavigationBarThemeData? Bottom navigation bar theme
bottomSheetTheme BottomSheetThemeData? BottomSheet theme
dialogTheme DialogThemeData? Dialog theme
package String? Resource package name
pageTransitionsTheme PageTransitionsTheme? Page transition animation
themeData ThemeData Assembly into Flutter native ThemeData

Call updateSystemUiOverlay() to apply the status bar style automatically.

1.2 FlThemeMode β€” Theme Mode Enum

enum FlThemeMode { light, dark, system }

// Properties
mode.isLight;   // bool
mode.isDark;    // bool
mode.isSystem;  // bool
mode.val;       // Display name

// Static parse
FlThemeMode.fromVal('Light'); // FlThemeMode.light

1.3 FlThemeWidget<T> β€” Theme Widget

Parameter Type Description
initialModel FlThemeMode Initial theme mode
lightTheme T Light theme data
darkTheme T Dark theme data
builder FlThemeBuilder<T> Builder callback (context, themeData) => Widget

Static methods:

// Get State
FlThemeWidget.of<T>(context);         // FlThemeState<T>, asserts if not found
FlThemeWidget.maybeOf<T>(context);    // FlThemeState<T>?, safe

// Set theme mode
FlThemeWidget.setupMode(context, FlThemeMode.dark);

// Read theme data
FlThemeWidget.watch<T>(context);      // Reactive (widget rebuilds)
FlThemeWidget.read<T>(context);       // One-time read (no rebuild)

// Force refresh
FlThemeWidget.reloadTheme(context);

1.4 ThemeContextExtension β€” Theme Context Extension

Must be used within FlThemeWidget subtree.

context.setupTheme(FlThemeMode.light);      // Set mode
FlThemeMode mode = context.themeMode;        // Read mode

// Reactive read (widget rebuilds on change)
final theme = context.wt<AppTheme>();

// One-time read (no rebuild)
final theme = context.rt<AppTheme>();

context.reloadTheme();  // Force refresh

2. Locale #

2.1 FlLocaleWidget β€” Locale Widget

FlLocaleWidget(
  initLocale: Locale('en', 'US'),
  builder: (context, locale) {
    return MaterialApp(
      locale: locale,
      localizationsDelegates: [...],
      supportedLocales: [...],
      home: const MyApp(),
    );
  },
)

Static methods:

FlLocaleWidget.of(context);       // FlLocaleWidgetState
FlLocaleWidget.maybeOf(context);  // FlLocaleWidgetState?

FlLocaleWidget.setupLocal(context, Locale('en'));

FlLocaleWidget.watch(context);   // Locale? (reactive)
FlLocaleWidget.read(context);    // Locale? (one-time)

2.2 LocaleContextExtension β€” Locale Context Extension

context.currentLocale = Locale('zh');  // Set locale
Locale? locale = context.wLocal;       // Reactive read
Locale? locale = context.rLocal;       // One-time read

3. BuildContext Extensions #

Available directly on any BuildContext.

// Theme shortcut
context.themeData;        // ThemeData

// Screen metrics
context.screenSize;       // Size
context.width;            // double
context.height;           // double
context.orientation;      // Orientation
context.isLandscape;      // bool
context.isPortrait;       // bool

// Widget geometry
context.renderBox;        // RenderBox?
context.size;             // Size?
context.localToGlobal(    // Offset?
  point: Offset.zero,
  ancestor: someRenderObject,
);

// Safe area & system UI
context.flutterView;           // FlutterView?
context.navigationBarHeight;   // double β€” AppBar height + status bar
context.windowPadding;         // EdgeInsets β€” keyboard & system UI insets
context.topSafeHeight;         // double β€” status bar height
context.bottomSafeHeight;      // double β€” home indicator area

// Route info
context.routeName;     // String?
context.arguments;     // Object?

4. GlobalKey Extensions #

final key = GlobalKey();

key.renderBox;    // RenderBox?
key.location;     // Offset? β€” screen position (top-left)
key.size;         // Size?

5. List Extensions #

// Sorting
[3, 1, 2].sortSelf(SortOrder.asc);   // [1, 2, 3] in-place
[3, 1, 2].sortSelf(SortOrder.desc);  // [3, 2, 1] in-place

list.sortBy((e) => e.toString().length);         // Sort by derived key (in-place)
list.sortedCopyBy((e) => e.toString().length);   // Sorted copy (original unchanged)

// Serialization
[1, 2, 3].toJsonString;  // '[1,2,3]'
[1, 2, 3].toJsonPretty;  // Pretty-printed JSON

// Safe access
[1, 2, 3].safeGet(5, defVal: 0);  // 0 (out of bounds)
[].safeRemoveLast();               // null (safe removal)

// Transformation & immutability
[1, 2, 3].mapList((e) => e.toString());  // ['1', '2', '3']
[1, 2, 3].unmodifiable;                   // Unmodifiable view
[1, 2, 3].copyList();                     // Shallow copy

6. Map Extensions #

// Serialization
{'a': 1}.toJsonString;   // '{"a":1}'
{'a': 1}.toJsonPretty;   // Pretty-printed JSON

// Safe access
{'a': 1}.safeGet('b', defVal: 0);  // 0 (key not found)

// Unmodifiable view
{'a': 1}.unmodifiable;

// Key/value lists
{'a': 1}.keyList;        // ['a']
{'a': 1}.valueList;      // [1]

// Filtering
{'a': 1, 'bb': 2}.whereKey((k) => k.length > 1);     // {'bb': 2}
{'a': 1, 'b': 2}.whereValue((v) => v > 1);            // {'b': 2}
{'a': 1, 'b': null}.removeNull;                        // {'a': 1}

7. ScrollNotification Extensions #

Use within NotificationListener<ScrollNotification>.

notification.minScrollExtent;      // double
notification.maxScrollExtent;      // double
notification.pixels;               // double
notification.viewportDimension;    // double
notification.fullScrollExtent;     // double
notification.axis;                 // Axis

notification.outOfRange;          // bool
notification.atEdge;              // bool
notification.canScroll;           // bool

notification.extentBefore;        // double
notification.extentInside;        // double
notification.extentAfter;         // double

notification.desc;                // String β€” formatted debug output

8. String Extensions #

// Blank checks
''.isBlank;       // true
'  '.isBlank;     // true
'a'.isNotBlank;   // true

// Validation
'user@example.com'.isEmail;     // true
'123'.isNumeric;                // true
'https://example.com'.isUrl;    // true

// Safe parsing
'42'.toInt();                   // 42
'abc'.toInt(defaultValue: -1);  // -1
'3.14'.toDouble();              // 3.14

// Text transformation
'hello'.capitalize;             // 'Hello'

// Null-safe checks (on String?)
String? s;
s.isNullOrEmpty;       // true
s.isNotNullOrEmpty;    // false
'  '.isNullOrEmpty;    // false (whitespace is not empty)

9. State Mixins #

All mixins are used via with in a State subclass.

9.1 StateSafeUpdateMixin β€” Safe setState

Call setState during the build phase without throwing. Automatically defers to post-frame callback.

class _MyPageState extends State<MyPage>
    with StateSafeUpdateMixin<MyPage> {

  void _onScroll() {
    safeSetState(() {
      _offset = newOffset;
    });
  }
}

9.2 TimelineMixin β€” Performance Tracing

Timeline tracing for debugging and profiling. Only works in Debug/Profile mode; no overhead in Release mode.

class MyService with TimelineMixin {
  void fetchData() {
    // Sync tracing
    startSyncTimeline('fetchData');
    // ... sync operation
    finishSyncTimeline();

    // Sync with automatic timing
    final result = timeSyncTimeline('compute', function: () {
      return heavyComputation();
    });

    // Async tracing
    timeAsyncTimeline('networkRequest', function: () async {
      return await http.get(url);
    });
  }
}
Method Description
startSyncTimeline(name, ...) Start sync tracing
finishSyncTimeline() Finish sync tracing
timeSyncTimeline<R>(name, function: ...) Sync operation + auto trace
startAsyncTimeline(name, ...) Start async tracing, returns TimelineTask?
finishAsyncTimeline(task, ...) Finish async tracing
timeAsyncTimeline<R>(name, function: ...) Async operation + auto trace

10. SystemUtils #

// Image cache
SystemUtils.clearImageCache();

// Keyboard control
SystemUtils.hideKeyboard();
SystemUtils.showKeyboard();
SystemUtils.dismissKeyboard();       // Via focus management
SystemUtils.clearClientKeyboard();

// Clipboard
await SystemUtils.copyToClipboardWithCallback('text', (result) => ...);
await SystemUtils.getClipboardData((result) => ...);

// Screen orientation
await SystemUtils.setOrientations([DeviceOrientation.portraitUp]);

// Text measurement
Size textSize = SystemUtils.calTextSize(
  context,
  text: 'Hello',
  style: TextStyle(fontSize: 16),
);

11. TimelineUtils #

Low-level API wrapping dart:developer.Timeline. Usually used via TimelineMixin.

// Sync tracing
TimelineUtils.startSync('eventName', tag: 'MyTag');
// ... operation
TimelineUtils.stopSync();

// Sync with automatic recording (returns result)
final result = TimelineUtils.timeSync<int>('compute', function: () => 42);

// Async tracing (auto start/stop)
final data = await TimelineUtils.timeAsync('fetch', function: () => api.get());

12. FutureMeasureUtils β€” Async Timing #

Measure the execution time of any async operation.

// Using extension method (preferred)
final measurement = await () => fetchData().measure();
print('Took ${measurement.duration}');

// Using static method
final result = await FutureMeasureUtils.measure(() => fetchData());

// With result extraction
final data = measurement.getOrThrow();    // Throws if failed
final safe = measurement.getOrDefault(    // Fallback on failure
  (error, stack) => fallbackData,
);

// Transform result
final parsed = measurement.map((raw) => Model.fromJson(raw));

TimeMeasureResult<T> properties:

Member Type Description
result T? Return value on success
duration Duration Elapsed time
error Object? Captured error on failure
isSuccess bool Whether it completed successfully
getOrThrow() T Returns result or throws captured error
map<R>() TimeMeasureResult<R> Transforms result on success
getOrDefault() T Returns result or handler's fallback

13. Widgets #

13.1 DecimalInputFormatter

Restricts TextField to valid decimal numbers with configurable precision.

TextField(
  inputFormatters: [
    DecimalInputFormatter(decimalDigits: 2),
  ],
  keyboardType: TextInputType.number,
)

Features:

  • Only allows digits 0-9 and .
  • Prevents multiple decimal points
  • Auto-prefix zero for leading dot (.5 β†’ 0.5)
  • Truncates excess decimal digits

13.2 KeyboardPadding

Automatically adds bottom padding equal to the keyboard height when the keyboard appears.

Scaffold(
  body: KeyboardPadding(
    duration: Duration(milliseconds: 300),
    curve: Curves.easeOut,
    child: Column(
      children: [
        Expanded(child: content),
        TextField(),
      ],
    ),
  ),
)

13.3 NoIndicatorBehavior

Removes overscroll glow/stretch indicators from scrollable views.

ScrollConfiguration(
  behavior: NoIndicatorBehavior(),
  child: ListView(children: [...]),
)

13.4 ScrollNotificationListener

Splits ScrollNotification into three separate callbacks.

ScrollNotificationListener(
  onStart: (notification) => print('Scroll started'),
  onUpdate: (notification) => print('Scrolling: ${notification.pixels}'),
  onEnd: (notification) => print('Scroll ended'),
  child: ListView.builder(itemCount: 100, itemBuilder: ...),
)

13.5 StatusBarColorView

Controls the status bar icon brightness for a subtree via AnnotatedRegion.

StatusBarColorView(
  brightness: Brightness.dark,  // Dark icons for light backgrounds
  child: Scaffold(...),
)

13.6 BottomNavBar β€” Bottom Navigation Container

Combines PageView (non-scrollable) with BottomNavigationBar.

BottomNavBar(
  pages: [HomePage(), SearchPage(), ProfilePage()],
  itemLabels: ['Home', 'Search', 'Profile'],
  itemBuilder: (context, index, isSelected) {
    final icons = [Icons.home, Icons.search, Icons.person];
    return Icon(icons[index],
        color: isSelected ? Colors.blue : Colors.grey);
  },
  controller: myController,
  onPageChanged: (event) => print('${event.from} β†’ ${event.to}'),
  transitionGuard: (from, to) async {
    if (to == 2 && !isLoggedIn) {
      await showLoginDialog();
      return isLoggedIn;
    }
    return true;
  },
)

Parameters:

Parameter Type Description
pages List<Widget> Page widgets
itemLabels List<String> Bottom navigation labels
itemBuilder NavigationItemBuilder Icon builder (context, index, isSelected) => Widget
controller NavigationController? Programmatic controller
initialIndex int Initial page index (default 0)
onPageChanged PageSwitchCallback? Page switch callback
themeData BottomNavigationBarThemeData? Theme override
transitionGuard TransitionGuard? Switch guard, return false to block

pages and itemLabels must have the same length.

Programmatic control:

final controller = NavigationController();
controller.jumpTo(1);
int current = controller.currentIndex;
controller.dispose();  // Clean up when no longer needed

Global singleton (for single BottomNavBar apps):

final controller = DefaultNavController();
controller.jumpTo(2);

13.7 SizeInfoNotifier β€” Layout Change Notifier

Monitors layout constraint/size changes via a custom RenderObject.

SizeInfoNotifier(
  index: 3,
  layoutChanged: (constraints, size) {
    print('New constraints: $constraints, size: $size');
  },
  child: Container(width: 100, height: 200, color: Colors.blue),
)

Bubble notification (parent listens):

NotificationListener<LayoutInfoNotification>(
  onNotification: (notification) {
    print('index:${notification.index}, size:${notification.size}');
    return false; // Don't block bubbling
  },
  child: SizeInfoNotifier(index: 0, child: yourWidget),
)

API Reference #

Category Core Types
Theme FlBaseThemeData, FlThemeMode, FlThemeWidget<T>, ThemeContextExtension
Locale FlLocaleWidget, FlLocaleWidgetState, LocaleContextExtension
Extensions ThemeExtensions (Context), CustomRouterExtension (Context), GlobalKeyExtension, ListSortExtensions, ListExtensions, MapExtensions, ScrollNotificationExtension, StringExtension, NullableStringExtension
Mixins StateSafeUpdateMixin, TimelineMixin
Utilities SystemUtils, TimelineUtils, FutureMeasureUtils, TimeMeasureResult<T>
Widgets DecimalInputFormatter, KeyboardPadding, NoIndicatorBehavior, ScrollNotificationListener, StatusBarColorView, BottomNavBar, SizeInfoNotifier, NavigationController, DefaultNavController

Enums #

Enum Values Description
SortOrder asc, desc List sort direction
FlThemeMode light, dark, system Theme mode

Typedefs #

Alias Signature Module
LocaleBuilder Widget Function(BuildContext, Locale?) Locale
FlThemeBuilder<T> Widget Function(BuildContext, T) Theme
CopyCallback void Function({required bool result}) SystemUtils
CopyDataCallback void Function({required String? result}) SystemUtils
NavigationItemBuilder Widget Function(BuildContext, int, bool) BottomNavBar
PageSwitchCallback void Function(PageSwitchEvent) BottomNavBar
TransitionGuard FutureOr<bool> Function(int, int) BottomNavBar
OnStart void Function(ScrollStartNotification) Widget
OnUpdate void Function(ScrollUpdateNotification) Widget
OnEnd void Function(ScrollEndNotification) Widget
LayoutChangedCallback void Function(BoxConstraints, Size) SizeInfoNotifier

License #

MIT

0
likes
160
points
23
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter toolkit providing theme management, locale switching, extension methods, state mixins, system utilities, and common widgets.

Repository (GitHub)
View/report issues

Topics

#theme #locale #utilities #widget #mixin

License

MIT (license)

Dependencies

flutter, meta

More

Packages that depend on fl_core_kit