vexio 1.0.0 copy "vexio: ^1.0.0" to clipboard
vexio: ^1.0.0 copied to clipboard

Vexio — The Ultimate Flutter HTTP Client. Zero dependencies, simple as http, powerful as Dio + Copper combined. Auto token refresh, caching, retry, WebSocket, GraphQL, offline queue, multipart, interc [...]

example/lib/main.dart

// ============================================================
//  example/lib/main.dart — Vexio Full Demo App
// ============================================================

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

// ── Models ────────────────────────────────────────────────────

class User {
  final int id;
  final String name;
  final String email;
  const User({required this.id, required this.name, required this.email});

  factory User.fromJson(dynamic json) => User(
        id:    (json['id'] as num).toInt(),
        name:  json['name']  as String,
        email: json['email'] as String,
      );

  @override
  String toString() => 'User($id, $name, $email)';
}

class Post {
  final int id;
  final String title;
  final String body;
  const Post({required this.id, required this.title, required this.body});

  factory Post.fromJson(dynamic json) => Post(
        id:    (json['id'] as num).toInt(),
        title: json['title'] as String,
        body:  json['body']  as String,
      );
}

// ── Main ──────────────────────────────────────────────────────

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

  await Vexio.init(
    baseUrl:             'https://jsonplaceholder.typicode.com',
    enableCache:         true,
    // Envelope keys — jsonplaceholder returns raw arrays/objects
    // so we treat any 2xx as success
    successKey:   'id',
    successValue: null, // raw responses — always treat 2xx as success
  );

  runApp(const VexioExampleApp());
}

// ── App ───────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'Vexio Demo',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        home: const HomePage(),
      );
}

// ── Home ──────────────────────────────────────────────────────

class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _tab = 0;

  final _pages = const [UsersPage(), PostsPage(), FeaturesPage()];

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('⚡ Vexio Demo'),
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        ),
        body: _pages[_tab],
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: _tab,
          onTap: (i) => setState(() => _tab = i),
          items: const [
            BottomNavigationBarItem(icon: Icon(Icons.people), label: 'Users'),
            BottomNavigationBarItem(icon: Icon(Icons.article), label: 'Posts'),
            BottomNavigationBarItem(icon: Icon(Icons.bolt), label: 'Features'),
          ],
        ),
      );
}

// ── Users Page — GET list ─────────────────────────────────────

class UsersPage extends StatefulWidget {
  const UsersPage({super.key});
  @override
  State<UsersPage> createState() => _UsersPageState();
}

class _UsersPageState extends State<UsersPage> {
  List<User> _users    = [];
  bool _loading        = false;
  String? _error;
  // final bool _fromCache      = false;

  @override
  void initState() {
    super.initState();
    _load();
  }

  Future<void> _load() async {
    setState(() { _loading = true; _error = null; });

    await Vexio.instance
        .get('/users',
            parser:    (json) => (json as List).map(User.fromJson).toList(),
            useCache:  true)
        .handle(
          onSuccess: (data, _) => setState(() {
            _users     = data ?? [];
            _loading   = false;
          }),
          onFailure: (msg, _) => setState(() {
            _error   = msg;
            _loading = false;
          }),
        );
  }

  @override
  Widget build(BuildContext context) {
    if (_loading) return const Center(child: CircularProgressIndicator());
    if (_error != null) {
      return Center(child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text('Error: $_error', style: const TextStyle(color: Colors.red)),
        ElevatedButton(onPressed: _load, child: const Text('Retry')),
      ],
    ));
    }

    return RefreshIndicator(
      onRefresh: _load,
      child: ListView.builder(
        itemCount: _users.length,
        itemBuilder: (_, i) {
          final u = _users[i];
          return ListTile(
            leading: CircleAvatar(child: Text('${u.id}')),
            title:   Text(u.name),
            subtitle: Text(u.email),
          );
        },
      ),
    );
  }
}

// ── Posts Page — GET with cancel token ───────────────────────

class PostsPage extends StatefulWidget {
  const PostsPage({super.key});
  @override
  State<PostsPage> createState() => _PostsPageState();
}

class _PostsPageState extends State<PostsPage> {
  List<Post>       _posts   = [];
  bool             _loading = false;
  String?          _error;
  VexioCancelToken? _token;

  @override
  void initState() {
    super.initState();
    _load();
  }

  Future<void> _load() async {
    _token?.cancel();
    _token = VexioCancelToken();
    setState(() { _loading = true; _error = null; });

    try {
      final res = await Vexio.instance.get('/posts',
        parser:      (json) => (json as List).map(Post.fromJson).toList(),
        cancelToken: _token,
        queryParams: {'_limit': 20},
      );
      if (mounted) {
        setState(() {
        _posts   = res.data ?? [];
        _loading = false;
      });
      }
    } on VexioCancelledException {
      // Navigated away
    } catch (e) {
      if (mounted) setState(() { _error = e.toString(); _loading = false; });
    }
  }

  @override
  void dispose() {
    _token?.cancel('Widget disposed');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_loading) return const Center(child: CircularProgressIndicator());
    if (_error != null) return Center(child: Text('Error: $_error'));

    return ListView.builder(
      itemCount: _posts.length,
      itemBuilder: (_, i) {
        final p = _posts[i];
        return ListTile(
          leading:  CircleAvatar(child: Text('${p.id}')),
          title:    Text(p.title, maxLines: 1, overflow: TextOverflow.ellipsis),
          subtitle: Text(p.body,  maxLines: 2, overflow: TextOverflow.ellipsis),
        );
      },
    );
  }
}

// ── Features Page — showcase all Vexio features ───────────────

class FeaturesPage extends StatefulWidget {
  const FeaturesPage({super.key});
  @override
  State<FeaturesPage> createState() => _FeaturesPageState();
}

class _FeaturesPageState extends State<FeaturesPage> {
  String _result = 'Tap a button to test a feature';
  bool   _loading = false;

  void _setResult(String r) =>
      setState(() { _result = r; _loading = false; });

  void _setLoading() => setState(() { _loading = true; });

  // ── Feature demos ─────────────────────────────────────────────

  Future<void> _testGet() async {
    _setLoading();
    final res = await Vexio.instance.get('/users/1', parser: User.fromJson);
    _setResult(res.isSuccess
        ? 'GET ✅\nUser: ${res.data?.name}\nTime: ${res.responseTime?.inMilliseconds}ms'
        : 'GET ❌ ${res.message}');
  }

  Future<void> _testPost() async {
    _setLoading();
    final res = await Vexio.instance.post('/posts',
      body: {'title': 'Vexio Post', 'body': 'Hello from Vexio', 'userId': 1},
    );
    _setResult(res.isSuccess
        ? 'POST ✅\nStatus: ${res.statusCode}'
        : 'POST ❌ ${res.message}');
  }

  Future<void> _testCache() async {
    _setLoading();
    // First call — network
    final t1 = DateTime.now();
    await Vexio.instance.get('/users/2', useCache: true);
    final net = DateTime.now().difference(t1).inMilliseconds;

    // Second call — cache
    final t2 = DateTime.now();
    final res = await Vexio.instance.get('/users/2',
        parser: User.fromJson, useCache: true);
    final cached = DateTime.now().difference(t2).inMilliseconds;

    _setResult('Cache demo ✅\n'
        'Network: ${net}ms\n'
        'Cache:   ${cached}ms\n'
        'fromCache: ${res.fromCache}');
  }

  Future<void> _testBatch() async {
    _setLoading();
    final results = await Vexio.instance.batch([
      () => Vexio.instance.get('/users/1', parser: User.fromJson),
      () => Vexio.instance.get('/users/2', parser: User.fromJson),
      () => Vexio.instance.get('/users/3', parser: User.fromJson),
    ]);
    final names = results
        .where((r) => r.isSuccess)
        .map((r) => (r.data as User).name)
        .join(', ');
    _setResult('Batch ✅\nFetched in parallel:\n$names');
  }

  Future<void> _testExtensions() async {
    _setLoading();
    String? name;
    int?    nameLength;

    await Vexio.instance
        .get('/users/4', parser: User.fromJson)
        .onSuccess((u) => name = u?.name)
        .mapData((u) => u.name.length)
        .handle(
          onSuccess: (len, _) => nameLength = len,
          onFailure: (msg, _) => _setResult('Extensions ❌ $msg'),
        );

    _setResult('Extensions ✅\nUser name:   $name\nName length: $nameLength');
  }

  Future<void> _testTypedErrors() async {
    _setLoading();
    try {
      await Vexio.instance.get('/users/99999999',
          parser:             User.fromJson);
      _setResult('No error thrown (server returned 2xx)');
    } on VexioNoInternetException {
      _setResult('VexioNoInternetException ✅');
    } on VexioTimeoutException {
      _setResult('VexioTimeoutException ✅');
    } on VexioServerException catch (e) {
      _setResult('VexioServerException ✅\nCode: ${e.statusCode}');
    } on VexioException catch (e) {
      _setResult('VexioException ✅\n${e.runtimeType}: ${e.message}');
    }
  }

  Future<void> _testCancel() async {
    _setLoading();
    final token = VexioCancelToken();
    final future = Vexio.instance.get('/posts',
        parser:      (j) => (j as List).length,
        cancelToken: token);

    // Cancel immediately
    token.cancel('Demo cancellation');

    try {
      await future;
      _setResult('Request completed before cancel');
    } on VexioCancelledException catch (e) {
      _setResult('Cancel ✅\nReason: ${e.message}');
    }
  }

  Future<void> _testConnectivity() async {
    _setLoading();
    final online = await Vexio.instance.isOnline();
    _setResult('Connectivity ✅\nOnline: $online');
  }

  Future<void> _testWebSocket() async {
    _setLoading();
    // Demo with a public echo WS server
    final ws = VexioWebSocket(
      url:           'wss://echo.websocket.events',
      autoReconnect: false,
    );

    try {
      await ws.connect().timeout(const Duration(seconds: 5));
      ws.send('Hello from Vexio!');
      final msg = await ws.messages.first
          .timeout(const Duration(seconds: 5));
      _setResult('WebSocket ✅\nEcho: $msg');
    } catch (e) {
      _setResult('WebSocket demo\n(No public echo server — '
          'use wss://your-server.com/ws in production)\n\nStatus: ${ws.status}');
    } finally {
      ws.dispose();
    }
  }

  // ── Build ─────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Result display
          Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color:        Colors.black87,
              borderRadius: BorderRadius.circular(12),
            ),
            child: _loading
                ? const Center(
                    child: CircularProgressIndicator(color: Colors.white))
                : Text(_result,
                    style: const TextStyle(
                        color: Colors.greenAccent,
                        fontFamily: 'monospace',
                        fontSize: 13)),
          ),
          const SizedBox(height: 16),

          // Feature buttons
          _FeatureButton('GET Request',       Icons.download,   _testGet),
          _FeatureButton('POST Request',      Icons.upload,     _testPost),
          _FeatureButton('Cache Demo',        Icons.cached,     _testCache),
          _FeatureButton('Batch Requests',    Icons.layers,     _testBatch),
          _FeatureButton('Fluent Extensions', Icons.link,       _testExtensions),
          _FeatureButton('Typed Exceptions',  Icons.error,      _testTypedErrors),
          _FeatureButton('Cancel Token',      Icons.cancel,     _testCancel),
          _FeatureButton('Connectivity',      Icons.wifi,       _testConnectivity),
          _FeatureButton('WebSocket',         Icons.cable,      _testWebSocket),
        ],
      ),
    );
  }
}

class _FeatureButton extends StatelessWidget {
  final String label;
  final IconData icon;
  final VoidCallback onTap;

  const _FeatureButton(this.label, this.icon, this.onTap);

  @override
  Widget build(BuildContext context) => Padding(
        padding: const EdgeInsets.only(bottom: 8),
        child: ElevatedButton.icon(
          onPressed: onTap,
          icon:      Icon(icon),
          label:     Text(label),
          style:     ElevatedButton.styleFrom(
            padding: const EdgeInsets.symmetric(vertical: 14),
          ),
        ),
      );
}
1
likes
150
points
10
downloads

Documentation

Documentation
API reference

Publisher

verified publishermysteriouscoder.com

Weekly Downloads

Vexio — The Ultimate Flutter HTTP Client. Zero dependencies, simple as http, powerful as Dio + Copper combined. Auto token refresh, caching, retry, WebSocket, GraphQL, offline queue, multipart, interceptors and more. Pure dart:io. No native setup required.

Repository (GitHub)
View/report issues

Topics

#http #networking #dio #rest #api

License

MIT (license)

Dependencies

flutter

More

Packages that depend on vexio