WT Framework — Flutter Edition

WondTech Logo

WT Framework - Flutter Edition v1.1
Inspired by the original WondTech PHP MVC Framework


Overview

A lightweight MVC framework that brings the simplicity and structure of the WT Framework — PHP Edition to Flutter mobile development.

It enforces a clean Model → Controller → View separation, built-in security helpers, session management, and a centralized router — so your Flutter app feels as organized as a well-structured PHP backend.


Features

  • MVC Architecture — Clear separation of Model, Controller, and View
  • Centralized Router — Define all routes in one place, with dynamic segments (e.g. /users/:id)
  • Base Model — HTTP GET / POST / PUT / DELETE with automatic JSON parsing
  • Base View — Synchronous and async views with built-in loading/error/empty states
  • WtSecurity — Input sanitization, XSS protection, encryption with secret key, secure token generation
  • WtSession — Persistent session management using SharedPreferences
  • WtHelper — Common utilities: date formatting, string manipulation, flash messages, dialogs
  • WtConfig — Centralized app configuration (base URL, secret key, theme)

What's new in v1.1

WtModel is now ready for real, authenticated, enveloped backends — not just bare REST:

  • Bearer auth, automatic — the token saved with WtSession.set('token', ...) is injected as Authorization: Bearer <token> on every request.
  • Response envelopes — set WtConfig.envelopeKey: 'data' and models transparently read { "state": true, "data": {...} }.
  • Real error messages — a failed response raises WtModelException carrying the server's msg (key configurable via WtConfig.messageKey); optional successKey treats 200 + {state:false} as a failure too.
  • Low-level clientgetJson / postJson / putJson / deleteJson / postMultipart for action-style endpoints (/api/login, /api/ad/5) and file uploads (WtUpload).
  • Safer defaults — client-side body sanitisation is now opt-in (WtConfig.sanitizeRequests) so it no longer mangles legitimate content; add per-model headers via the extraHeaders getter.
WtConfig.init(const WtConfig(
  appName: 'Findlly',
  baseUrl: 'https://findlly.co',
  secretKey: '...',
  envelopeKey: 'data',   // unwrap { state, data }
  successKey: 'state',   // 200 + state:false => error
  tokenKey: 'token',     // WtSession key holding the bearer token
));

// action endpoint + upload, all token- & envelope-aware:
class AdsModel extends WtModel<Ad> {
  @override String get endpoint => '/api/ads';
  @override Ad fromJson(Map<String, dynamic> j) => Ad.fromJson(j);

  Future<List<Ad>> search(String q) async =>
      fromJsonList((await getJson('/api/ads', query: {'q': q}))['items']);

  Future<Ad> createWithPhotos(Map<String, String> fields, List<int> jpg) async =>
      fromJson(await postMultipart('/api/createad',
        fields: fields,
        files: [WtUpload(field: 'images[]', bytes: jpg, filename: 'p.jpg', contentType: 'image/jpeg')],
      ));
}

Project Structure

wt_framework/
├── lib/
│   ├── wt.dart          ← Package entry point
│   └── libs/
│       ├── core/
│       │   ├── wt_app.dart        ← App root widget
│       │   └── wt_router.dart     ← Route dispatcher
│       ├── config/
│       │   └── wt_config.dart     ← Global config
│       ├── mvc/
│       │   ├── wt_controller.dart ← Base Controller
│       │   ├── wt_model.dart      ← Base Model + HTTP
│       │   └── wt_view.dart       ← Base View
│       └── helpers/
│           ├── wt_security.dart   ← Security utilities
│           ├── wt_session.dart    ← Session manager
│           └── wt_helper.dart     ← General helpers
│
example/
├── main.dart
└── lib/
    ├── controllers/
    ├── models/
    └── views/

Installation

Add to your pubspec.yaml:

dependencies:
  wt_framework:
    git:
      url: https://github.com/mogbil/WT_Flutter_FrameWork.git

Then run:

flutter pub get

Then import in your Dart files:

import 'package:wt_framework/wt.dart';

Usage

1. Initialize the App — main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await WtSession.init(); // equivalent to session_start()

  WtConfig.init(const WtConfig(
    appName: 'My App',
    baseUrl: 'https://api.example.com',
    secretKey: 'your_secret_key',
  ));

  runApp(WtApp(
    config: WtConfig.instance,
    router: WtRouter(
      initialRoute: '/',
      routes: [
        WtRoute(path: '/',           builder: (s) => HomeController(s)),
        WtRoute(path: '/login',      builder: (s) => LoginController(s)),
        WtRoute(path: '/users',      builder: (s) => UsersController(s)),
        WtRoute(path: '/users/:id',  builder: (s) => UserDetailController(s)),
      ],
    ),
  ));
}

2. Define a Model

class UserModel extends WtModel<User> {
  @override
  String get endpoint => '/users';

  @override
  User fromJson(Map<String, dynamic> json) => User(
    id: json['id'],
    name: json['name'],
    email: json['email'],
  );
}

// Fetch all users
final users = await UserModel().fetchAll();

// Fetch one
final user = await UserModel().fetch(params: {'id': '1'});

// Create
await UserModel().create({'name': 'Ali', 'email': 'ali@example.com'});

// Update
await UserModel().update('1', {'name': 'Ali Updated'});

// Delete
await UserModel().delete('1');

3. Define a Controller

class UsersController extends WtController {
  UsersController(super.settings);

  @override
  WtView view(BuildContext context) {
    final v = UsersView();
    v.assign('title', 'All Users');
    v.assign('onTap', (int id) {
      navigate(context, '/users/:id', args: {'id': id.toString()});
    });
    return v;
  }
}

4. Define a View

Synchronous:

class HomeView extends WtView {
  @override
  Widget build(BuildContext context) {
    return scaffold(
      context: context,
      body: Center(child: Text(title)),
    );
  }
}

Async (loads data before rendering):

class UsersView extends WtAsyncView<List<User>> {
  @override
  Future<List<User>> loadData() => UserModel().fetchAll();

  @override
  Widget buildData(BuildContext context, List<User> users) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: ListView(
        children: users.map((u) => ListTile(title: Text(u.name))).toList(),
      ),
    );
  }
}

5. Security

// Sanitize input — protection against XSS and SQL injection
final safe = WtSecurity.sanitize(formData);

// Hash data with your secret key
final hash = WtSecurity.hashWithKey(data, WtConfig.instance.secretKey);

// Encode / Decode
final encoded = WtSecurity.encode(data, secretKey);
final decoded = WtSecurity.decode(encoded, secretKey);

// Validate
WtSecurity.isValidEmail('user@example.com'); // true/false
WtSecurity.isValidInput(userInput);

// Generate secure token
final token = WtSecurity.generateToken(); // 32-char random string

6. Session Management

// Login — saves user to session
await WtSession.login({'id': 1, 'name': 'Ali', 'email': 'ali@example.com'});

// Check login state
WtSession.isLoggedIn(); // true/false

// Get logged-in user data
final user = WtSession.getUser();

// Store and retrieve custom values
await WtSession.set('cart_count', 5);
final count = WtSession.get<int>('cart_count');

// Logout — clears session
await WtSession.logout();

7. Helpers

// Flash messages (success / error)
WtHelper.flash(context, 'Saved successfully!');
WtHelper.flash(context, 'Something went wrong', isError: true);

// Loading dialog
WtHelper.showLoading(context);
WtHelper.hideLoading(context);

// Confirm dialog
final confirmed = await WtHelper.confirm(context,
  title: 'Delete',
  message: 'Are you sure?',
);

// Format date
WtHelper.formatDate(DateTime.now()); // "2024-01-15"
WtHelper.timeAgo(someDate);          // "3h ago"

// Format number
WtHelper.currency(1999.5);           // "$1999.50"
WtHelper.formatNumber(1000000);      // "1,000,000"

// String utilities
WtHelper.truncate('Long text...', 20);
WtHelper.ucFirst('hello world');     // "Hello world"
WtHelper.slug('My Page Title');      // "my-page-title"

License

MIT License © 2026 WT Framework — Flutter Edition v1.1 — Built by WondTech. All rights reserved.