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

A versatile and lightweight Flutter toolkit for state management, shell routing, dependency injection, responsive UI, MVVM, and form validation to speed up development.

htlib #

A versatile and lightweight toolkit for Flutter, providing a collection of essential utilities to accelerate your development process. htlib bundles common patterns like state management, routing, dependency injection, and responsive UI into a single, cohesive package.

Features #

  • Reactive State Management: A simple, powerful, and fine-grained reactivity system inspired by MobX/Vue, featuring Observable, Computed, and Observer widgets.
  • Declarative Shell-based Routing: A robust routing solution that makes nested navigation (e.g., with a BottomNavigationBar) intuitive and easy to manage using ShellRouter and ShellOutlet.
  • Simple Dependency Injection: A lightweight service locator for managing your application's dependencies with singleton and factory patterns.
  • Responsive UI Toolkit: A set of widgets and extensions (ResponsiveGrid, ResponsiveWidget) to easily build adaptive layouts for mobile, tablet, and desktop.
  • MVVM Architecture: A basic implementation of the Model-View-ViewModel pattern (ViewModel, ViewWidget) to help structure your UI logic.
  • Form Validation: A chainable and extensible validator system for your form fields.
  • Dynamic Page Title: A widget to easily manage the app's title in the OS task switcher for web and desktop.

Installation #

Add htlib to your pubspec.yaml file:

flutter pub add htlib

Then, run flutter pub get in your terminal.

Usage #

1. Reactive State Management #

Create reactive state that automatically updates your UI when it changes.

  • Observable<T>: A container for a value that can be observed.
  • Computed<T>: A value derived from other observables. It's cached and only re-evaluates when its dependencies change.
  • Observer: A widget that rebuilds automatically when any observables it uses are modified.
  • ObservableArray<T>: A reactive list that notifies listeners when its contents are modified.

Example:

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

// 1. Create your observables
final counter = Observable(0);
final isEven = Computed(() => counter.value.isEven);

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        // 3. Use Observer to rebuild the UI on state changes
        child: Observer(
          builder: (_) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('Count: ${counter.value}'), // or just Text('Count: $counter')
                Text('Is Even: ${isEven.value}'), // or just Text('Is Even: $isEven')
              ],
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // 2. Modify the state, and the UI will update automatically
        onPressed: () => counter.value++,
        child: Icon(Icons.add),
      ),
    );
  }
}

2. Dependency Injection (Injection) #

A simple service locator to register and retrieve your application's services.

Example:

import 'package:htlib/htlib.dart';

// 1. Define your services
class ApiService {
  Future<String> fetchData() async => 'Hello from API';
}

class AuthRepository {
  final ApiService _api;
  AuthRepository(this._api);
}

// 2. Create an injection instance
final injection = Injection();

void setupDependencies() {
  // 3. Register dependencies
  // Singleton: one instance throughout the app's lifecycle
  injection.addSingleton<ApiService>(() => ApiService());

  // Factory: a new instance is created on every request
  injection.addFactory<AuthRepository>(() => AuthRepository(injection.get<ApiService>()));
}

void main() {
  setupDependencies();

  // 4. Retrieve dependencies
  final api = injection.get<ApiService>();
  // Or using the shorthand:
  final authRepo = injection<AuthRepository>();

  print(api);
  print(authRepo);
}

3. Declarative Routing (ShellRouter) #

Manage nested navigation (e.g., Scaffold with a BottomNavigationBar) using a shell-based approach. The ShellOutlet widget acts as a placeholder for child routes.

Example:

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

// 1. Define your routes
final router = ShellRouter(
  // The 'container' is the shell that wraps your child pages
  container: (context) => AppShell(),
  routes: [
    ShellRoute(
      path: 'a',
      builder: (context, params) => const Center(child: Text('Page A')),
    ),
    ShellRoute(
      path: 'b',
      // This route has a nested child route
      builder: (context, params) => const ShellOutlet(), // Use ShellOutlet for nested routes
      children: [
        ShellRoute(
            path: ':id',
            builder: (context, params) =>
                Center(child: Text('Detail for B with ID: ${params['id']}'))),
      ],
    ),
  ],
);

void main() => runApp(MaterialApp.router(routerConfig: router));

// 2. Create your Shell UI with a ShellOutlet
class AppShell extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Shell Router Example")),
      // The ShellOutlet renders the matched child route's widget here
      body: const ShellOutlet(),
      bottomNavigationBar: BottomNavigationBar(
        // Use locationOf to update UI based on the current route
        currentIndex: ShellRouter.locationOf(context).startsWith('/b') ? 1 : 0,
        onTap: (index) {
          if (index == 0) router.navigate(Uri.parse('/a'));
          if (index == 1) router.navigate(Uri.parse('/b/123')); // Navigate to a nested route
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Page A'),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: 'Page B'),
        ],
      ),
    );
  }
}

4. Responsive UI Toolkit #

Build adaptive UIs with a powerful 12-column grid system and responsive helpers.

  • context.responsive<T>(): Select a value based on screen size (mobile, tablet, desktop).
  • ResponsiveWidget: Select a widget builder based on screen size.
  • ResponsiveGrid: Arranges children in a responsive 12-column layout.

Example:

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

class ResponsiveScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Example 1: Get a responsive value
    final double padding = context.responsive<double>(
      mobile: 8.0,
      tablet: 16.0,
      desktop: 24.0,
    );

    // Example 2: Use the ResponsiveGrid
    return Scaffold(
      appBar: AppBar(title: Text('Responsive Grid')),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(padding),
        child: ResponsiveGrid(
          columnSpacing: 12,
          rowSpacing: 12,
          children: [
            ResponsiveItem.span(
              child: Container(color: Colors.red, height: 100),
              mobile: 12,   // Full width on mobile
              tablet: 6,    // Half width on tablet
              desktop: 4,   // One-third width on desktop
            ),
            ResponsiveItem.span(
              child: Container(color: Colors.green, height: 100),
              mobile: 12,
              tablet: 6,
              desktop: 4,
            ),
            ResponsiveItem.span(
              child: Container(color: Colors.blue, height: 100),
              mobile: 12,
              tablet: 12,
              desktop: 4,
            ),
          ],
        ),
      ),
    );
  }
}

5. MVVM Pattern (ViewModel & ViewWidget) #

Separate your UI from your business logic using the Model-View-ViewModel pattern. The ViewModel holds the state and logic, and updateView() triggers a rebuild of the ViewWidget.

Example:

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

// 1. Define your ViewModel
class CounterViewModel extends ViewModel {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    updateView(); // This will trigger a rebuild of the view
  }
}

// 2. Create your ViewWidget, which connects the ViewModel to the UI
class CounterView extends ViewWidget<CounterViewModel> {
  const CounterView({super.key});

  @override
  CounterViewModel createModel() => CounterViewModel();

  @override
  Widget build(BuildContext context, CounterViewModel model) {
    return Scaffold(
      body: Center(
        child: Text('Count: ${model.count}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: model.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

6. Form Validation #

Chain multiple validation rules easily for TextFormField and other form inputs.

Example:

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

class MyForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Form(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: TextFormField(
          decoration: InputDecoration(labelText: 'Email'),
          autovalidateMode: AutovalidateMode.onUserInteraction,
          // Chain multiple validators using Validator.rules
          validator: Validator.rules([
            Required(message: 'Please enter an email'),
            Email(message: 'Please enter a valid email'),
          ]),
        ),
      ),
    );
  }
}

7. Page Title Management #

Easily set the application's title in the OS task manager on desktop and web builds. It intelligently handles nested PageTitle widgets.

Example:

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

void main() {
  // Set a global app name to be appended to all titles (optional)
  PageTitle.appName = 'My Awesome App';
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: HomeScreen());
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // This will set the window title to "Home | My Awesome App"
    return PageTitle(
      title: 'Home',
      child: Scaffold(
        appBar: AppBar(title: Text('Home Screen')),
        body: Center(child: Text('Welcome!')),
      ),
    );
  }
}
0
likes
160
points
24
downloads

Publisher

unverified uploader

Weekly Downloads

A versatile and lightweight Flutter toolkit for state management, shell routing, dependency injection, responsive UI, MVVM, and form validation to speed up development.

Repository (GitHub)
View/report issues

Topics

#state-management #routing #dependency-injection #responsive #mvvm

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on htlib