utiler 1.4.4 copy "utiler: ^1.4.4" to clipboard
utiler: ^1.4.4 copied to clipboard

Essential utility functions and helpers for Dart/Flutter projects. Simplify everyday coding with tools for common logic patterns. Developed by Mehrab Ghassab

Utiler #

A comprehensive Dart/Flutter utility toolkit — async handling, concurrency, logging, storage, networking, UI helpers, and global app configuration in one package.

pub package License: MIT

import 'package:utiler/utiler.dart';

Table of Contents #

Module What it does
UtilerScope Single entry point for theme, locale, logging, config & lifecycle
Async Utilities Debouncer, Throttler, and Retry for rate-limiting and resilience
Concurrency Utilities Sequential and parallel task execution
Core Utilities Either, Guard, AsyncGuard, TimedCache, connectivity, config & flags
Form Validation Chainable sync and async validators for TextFormField
Database Layer Unified JSON and secure key-value storage
API Service Typed HTTP client with parser registry and Either responses
Logging System Structured logging with in-app console and file export
Performance Monitor Live glassmorphism overlay with real-time app metrics
UI Utilities Spacing helpers, safe area, keyboard dismiss, and responsive scaling
Extensions String, num, list, map, DateTime, and context extension methods
Feature Generator (CLI) CLI tool to scaffold Clean Architecture features

🌍 UtilerScope — Single Entry Point #

UtilerScope is the top-level widget that wires together theme, locale, logging, feature flags, app config, and lifecycle in a single place. Wrap your runApp with it and everything is ready throughout your widget tree.

Setup #

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

void main() {
  runApp(
    UtilerScope(
      // Logging
      enabledLog: true,
      exportLog: false,
      showLogWidget: false, // dev-only: logger widget
      showPerformanceMonitor: false, // dev-only: live metrics overlay

      // Theming — loads every .json file under the directory
      jsonThemesAddress: 'assets/theme',
      themeAnimation: ValuesAnimationType.circle,
      themeAnimationDuration: Duration(milliseconds: 400),

      // Localization — loads every .json file under the directory
      jsonLocalesAddress: 'assets/locale',
      localeAnimation: ValuesAnimationType.blurReveal,
      localeAnimationDuration: Duration(milliseconds: 400),

      // Feature flags
      featureFlags: const {
        'new_checkout': true,
        'beta_chat': false,
      },

      // Connectivity
      onConnectivityChange: (status) {
        if (status == InternetStatus.disconnected) {
          debugPrint('No internet access');
        }
      },

      // App config
      appConfig: AppConfigStore(
        active: AppEnvironment.production,
        configs: {
          AppEnvironment.development: AppConfig.fromMap(
            environment: AppEnvironment.development,
            data: {'api_base_url': 'http://localhost:8080'},
          ),
          AppEnvironment.production: AppConfig.fromMap(
            environment: AppEnvironment.production,
            data: {'api_base_url': 'https://api.example.com'},
          ),
        },
      ),

      // Lifecycle
      lifecycleListener: (state) {
        debugPrint('App lifecycle: $state');
      },

      child: const MyApp(),
    ),
  );
}

Change Theme & Locale #

// With BuildContext
context.changeAppTheme('dark');
context.changeAppLocale('en');

// Per-call animation override
context.changeAppTheme('dark', ValuesAnimationType.circle);
context.changeAppLocale('fa', ValuesAnimationType.blurReveal);

// Without BuildContext
Utiler.changeAppTheme('dark', ValuesAnimationType.circle);
Utiler.changeAppLocale('en');

Animation priority: per-call animationUtiler default → instant when both are null.

await Utiler.changeThemeAnimation(ValuesAnimationType.circle);
await Utiler.changeThemeAnimation(null); // instant

Access Values #

context.appJsonTheme;
context.appJsonLocale;
'home.background'.cr;  // color from JSON theme
'home.appbar'.tr;      // localized string

// All available values (typed lists, or JSON maps keyed by id)
Utiler.allThemes;       // List<ThemeValues>?
Utiler.allJsonThemes;   // {'light': {...}, 'dark': {...}}
Utiler.allLocales;      // List<LocaleValues>?
Utiler.allJsonLocales;  // {'en': {...}, 'fa': {...}}

Access App Config #

final url = Utiler.config.active.require<String>('api_base_url');
final timeout = Utiler.config.active.get<int>('timeout_seconds', fallback: 10);

Access Feature Flags #

if (Utiler.flags.isEnabled('new_checkout')) {
  // show new flow
}

⏱ Async Utilities #

Debouncer — delay rapid calls (e.g. search input, typing):

final debouncer = Debouncer(400);

void onSearchChanged(String query) {
  debouncer(() => fetchResults(query));
}

// Execute immediately without waiting for the delay (e.g. on form submit)
debouncer.flush();

Throttler — limit how often an action runs:

final throttler = Throttler(1000);

void onButtonTap() {
  throttler(() => submitForm());
}

Retry — retry flaky async work:

final retry = Retry();

// Returns null on exhaustion
final result = await retry.call<String>(
  () => unstableNetworkCall(),
  maxAttempts: 5,
  delayMilliseconds: 400,
  onError: (error, attempt) => debugPrint('Attempt $attempt failed: $error'),
);

// Throws the last error on exhaustion instead of returning null
final data = await retry.callOrThrow<String>(
  () => unstableNetworkCall(),
  maxAttempts: 3,
);

⚡ Concurrency Utilities #

BatchExecutor — run tasks sequentially:

const executor = BatchExecutor();
final results = await executor.execute([
  () async => await loadUsers(),
  () async => await loadPosts(),
]);

ParallelExecutor — run tasks concurrently:

final executor = ParallelExecutor();
final results = await executor.execute<String>([
  () async => fetchProfile(),
  () async => fetchFeed(),
  () async => fetchNotifications(),
]);

🧠 Core Utilities #

InternetConnectivity — check and listen to network status:

final status = await InternetConnectivity.currentStatus;

InternetConnectivity.onStatusChange.listen((status) {
  debugPrint('Network: $status');
});

Either — functional error handling:

Either<String, int> result = Right(42);

result.fold(
  (error) => debugPrint('Error: $error'),
  (value) => debugPrint('Success: $value'),
);

// Transform the right value
result.map((n) => n * 2);                        // Right(84)
result.mapLeft((e) => e.toUpperCase());          // Left('ERROR')
result.flatMap((n) => n > 0 ? Right(n) : Left('negative'));
result.getOrElse(0);                             // 42 or fallback

Guard — safe sync execution (returns null on error):

final value = Guard<int>()(() => int.parse(userInput));

AsyncGuard — same as Guard but for async functions:

final data = await AsyncGuard<String>()(() async => fetchData());
// returns null if fetchData() throws, instead of propagating

TimedCache — in-memory cache with per-entry TTL:

final cache = TimedCache<String, User>(ttl: const Duration(minutes: 5));

cache.set('user_42', user);
final cached = cache.get('user_42'); // null after TTL expires
cache.evictExpired();

LazyValue — compute once, cache the result:

final lazy = LazyValue<int>(() async => heavyComputation());
final first = await lazy.value;  // computed
final second = await lazy.value; // cached

LifecycleHandler — observe app lifecycle:

LifecycleHandler(
  lifecycleListener: (state) => debugPrint('Lifecycle: $state'),
  child: const MyApp(),
)

AppConfig — typed environment configuration:

final config = AppConfig.fromMap(
  environment: AppEnvironment.production,
  data: {
    'api_base_url': 'https://api.example.com',
    'timeout_seconds': 30,
  },
);

final url = config.require<String>('api_base_url');
final timeout = config.get<int>('timeout_seconds', fallback: 10);

AppConfigStore — switch between dev/staging/prod configs:

final store = AppConfigStore(
  active: AppEnvironment.development,
  configs: {
    AppEnvironment.development: devConfig,
    AppEnvironment.production: prodConfig,
  },
);

final apiUrl = store.active.require<String>('api_base_url');

FeatureFlags — runtime feature toggles:

final flags = FeatureFlags({
  'new_checkout': true,
  'beta_chat': false,
});

if (flags.isEnabled('new_checkout')) {
  // show new flow
}

✅ Form Validation #

FormValidator — chainable rules for TextFormField:

TextFormField(
  validator: FormValidator().required().email().build(),
)

// optional() skips all rules when the field is blank
TextFormField(
  validator: FormValidator().optional().email().minLength(6).build(),
)

AsyncFormValidator — for rules that require async work (e.g. server-side uniqueness checks):

final validator = AsyncFormValidator()
  .required()
  .email()
  .rule((value) async {
    final taken = await api.isEmailTaken(value!);
    return taken ? 'Email already in use' : null;
  });

final error = await validator.validate('user@example.com');

💾 Database Layer #

Import: package:utiler/src/database/database.dart

Database — unified JSON + secure storage:

import 'package:utiler/src/database/database.dart';
import 'package:utiler/src/database/json_database_data.dart';
import 'package:utiler/src/database/secure_database_data.dart';

final db = Database();
await db.init(
  logging: true,
  jsonStoragePath: '/path/from/your/app',
);

// JSON storage
await db.putJson(
  JsonDatabaseData(key: 'settings', data: {'theme': 'dark'}),
);
final settings = await db.getJson('settings');

// Secure storage
await db.putSecure(
  SecureDatabaseData(key: 'token', value: 'secret_token'),
);
final token = await db.getSecure('token');

🌐 API Service #

Typed HTTP client with a parser registry and Either-style responses.

import 'package:http/http.dart' as http;
import 'package:utiler/utiler.dart';

final api = ApiService<AppError>(
  client: http.Client(),
  parsers: [PostParser(), UserParser()],
  errorParser: AppErrorParser(),
  baseUrl: 'https://api.example.com',
  logging: true,
);

final response = await api.get<Post>('/posts/1');

response.result.fold(
  (error) => debugPrint(error.message),
  (post) => debugPrint(post.title),
);

ApiModel + ApiParser

class Post extends ApiModel<Post, PostParser> {
  const Post({required this.id, required this.title});
  final int id;
  final String title;

  @override
  List<Object?> get props => [id, title];
}

class PostParser extends ApiParser<Post> {
  @override
  Post fromJson(Map<String, dynamic> json) => Post(
        id: json['id'] as int,
        title: json['title'] as String,
      );

  @override
  Map<String, dynamic> toJson(Post model) => {
        'id': model.id,
        'title': model.title,
      };
}

📊 Logging System #

Logger

Logger.enabled = true;
Logger.showWidget = true;
Logger.exportDirectory = '/path/from/your/app';
Logger.export = true;
await Logger.i('App started', tag: 'BOOT');

PrettyLogger

await PrettyLogger.s('Saved successfully', tag: 'API');
await PrettyLogger.e('Request failed', tag: 'API');

StopwatchLogger — measure async duration:

StopwatchLogger(
  'fetch_users',
  api.get<User>('/users'),
);

LoggerConsole — in-app log viewer:

LoggerConsole(
  child: MaterialApp(home: HomePage()),
)

📈 Performance Monitor #

A live glassmorphism overlay with nine real-time app metrics. All values are app-only — no native code required, works on all platforms.

Metric Source
FPS, jank count, total frames SchedulerBinding frame timings
Build time, raster time, vsync overhead FrameTiming
GPU raster cache FrameTiming.layerCacheBytes
Memory (current & peak RSS) dart:io ProcessInfo · N/A on web

Enable via UtilerScope:

UtilerScope(showPerformanceMonitor: true, child: const MyApp())

Or wrap directly:

PerformanceMonitor(child: const MyApp())

🎨 UI Utilities #

Gaps & spacing

Column(children: [Text('Hello'), 16.v, Text('World')]);
Row(children:   [Icon(Icons.star), 8.h, Text('Rated')]);

ColorfulSafeArea — colored safe area with padding control:

ColorfulSafeArea(
  color: Colors.white,
  maintainBottomViewPadding: true,
  child: Scaffold(body: HomePage()),
)

KeyboardDismiss — tap outside to close the keyboard:

KeyboardDismiss(child: LoginForm())

InkwellButton — custom ink-well button:

InkwellButton(
  borderRadius: 12,
  onPressed: () => debugPrint('tapped'),
  child: const Text('Tap me'),
)

ExpandableWidget — animated expand/collapse:

ExpandableWidget(
  expand: isExpanded,
  child: const Text('Hidden content'),
)

ConnectivityWidget — rebuilds automatically when internet status changes:

ConnectivityWidget(
  connected: (context) => const OnlineContent(),
  disconnected: (context) => const OfflineBanner(),
  vpn: (context) => const VpnNotice(), // optional
)

Responsive — scale sizes to screen:

final width = Responsive.of(context).scale(100);

🔌 Extensions #

String

'hello world'.toTitleCase;    // Hello World
'hello world'.toSnakeCase;    // hello_world
'123'.toIntOrNull;            // 123
'123'.toPersianDigits();      // ۱۲۳
'FF5733'.toColor;             // Color

Num

123.toPersianNumber;          // ۱۲۳
5.isBetween(1, 10);           // true
180.toRadians;

List / Iterable / Map

[1, 1, 2, 3].unique;
[1, 2, 3].firstOrNull;
{'a': 1}.merge({'b': 2});

DateTime

DateTime.now().isToday;        // true
DateTime.now().isPast;         // false
someDate.timeAgo;              // '3 hours ago'
someDate.format('yyyy/MM/dd'); // '2026/06/19'
someDate.startOfDay;           // 2026-06-19 00:00:00
someDate.isSameDay(otherDate); // true / false

Context

context.isPortrait;
context.size.width;
context.size.height;

🛠 Feature Generator (CLI) #

Generate Clean Architecture features from the command line:

dart run utiler:create_feature -n feature_name -b -r
Flag Description
-n, --name Feature name (required)
-b, --use-bloc Add Bloc state management
-r, --use-riverpod Add Riverpod dependency injection

Generates a ready-to-use structure inside lib/features/.


📦 Examples #

Runnable demos for every module live in the example/ folder:

Folder Covers
example/async/ Debouncer (+ flush), Throttler, Retry (+ callOrThrow, onError)
example/concurrency/ Batch & parallel executors
example/core/ Either (+ map/flatMap), Guard, AsyncGuard, TimedCache, LazyValue, AppConfig, FeatureFlags, ConnectivityWidget
example/validation/ FormValidator (+ optional), AsyncFormValidator
example/database/ JSON & secure storage
example/extension/ String, num, list, map, DateTime helpers
example/logger/ Logger, PrettyLogger, StopwatchLogger
example/service/ ApiService
example/ui/ Widgets and layout helpers
example/main.dart UtilerScope demo (with PerformanceMonitor)

💡 Why Utiler? #

  • All-in-one utility toolkit — one dependency, everything included
  • Reduces boilerplate and improves code readability
  • Clean Architecture friendly
  • Production-ready design for Flutter apps

📫 Contact #

3
likes
160
points
27
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Essential utility functions and helpers for Dart/Flutter projects. Simplify everyday coding with tools for common logic patterns. Developed by Mehrab Ghassab

Repository (GitHub)
View/report issues

Topics

#flutter #utilities #localization #theming #api

License

MIT (license)

Dependencies

connectivity_plus, connectivity_plus_platform_interface, flutter, flutter_secure_storage, hive_ce, http, path, web

More

Packages that depend on utiler