utiler 1.3.0 copy "utiler: ^1.3.0" to clipboard
utiler: ^1.3.0 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, LazyValue, connectivity, app config & feature flags
Form Validation Chainable, reusable 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
UI Utilities Spacing helpers, safe area, keyboard dismiss, and responsive scaling
Extensions String, num, list, map, 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,

      // Theming
      jsonThemesAddress: const [
        'assets/theme/light.json',
        'assets/theme/dark.json',
      ],
      themeAnimation: ValuesAnimationType.circle,
      themeAnimationDuration: Duration(milliseconds: 400),

      // Localization
      jsonLocalesAddress: const [
        'assets/locale/en.json',
        'assets/locale/fa.json',
      ],
      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

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));
}

Throttler — limit how often an action runs:

final throttler = Throttler(1000);

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

Retry — retry flaky async work:

final retry = Retry();
final result = await retry.call<String>(
  () => unstableNetworkCall(),
  maxAttempts: 5,
  delayMilliseconds: 400,
);

⚡ 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'),
);

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

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

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:

final error = FormValidator()
  .required()
  .email()
  .minLength(8)
  .validate('user@example.com');

TextFormField(
  validator: FormValidator()
    .required()
    .iranianPhone()
    .validate(),
)

💾 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()),
)

🎨 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'),
)

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});

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, Throttler, Retry
example/concurrency/ Batch & parallel executors
example/core/ Either, Guard, LazyValue, connectivity, AppConfig, FeatureFlags
example/validation/ FormValidator
example/database/ JSON & secure storage
example/extension/ String, num, list, map helpers
example/logger/ Logger, PrettyLogger, StopwatchLogger
example/service/ ApiService
example/ui/ Widgets and layout helpers
example/main.dart UtilerScope demo

💡 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
0
points
36
downloads

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

unknown (license)

Dependencies

connectivity_plus, flutter, flutter_secure_storage, hive, http, path

More

Packages that depend on utiler