autopilot_api 1.0.1 copy "autopilot_api: ^1.0.1" to clipboard
autopilot_api: ^1.0.1 copied to clipboard

Zero-boilerplate smart API engine for Flutter. Only uses http + shared_preferences.

example/lib/main.dart

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

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

class UserModel {
  final int id;
  final String name;
  final String email;
  final String username;

  const UserModel({
    required this.id,
    required this.name,
    required this.email,
    required this.username,
  });

  factory UserModel.fromJson(dynamic json) => UserModel(
        id       : json['id'] as int,
        name     : json['name'].toString(),
        email    : json['email'].toString(),
        username : json['username'].toString(),
      );

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

class PostModel {
  final int id;
  final String title;
  final String body;

  const PostModel({required this.id, required this.title, required this.body});

  factory PostModel.fromJson(dynamic json) => PostModel(
        id    : json['id'] as int,
        title : json['title'].toString(),
        body  : json['body'].toString(),
      );

  @override
  String toString() => 'Post(id:$id\n     title:$title)';
}

// ─── Entry Point ──────────────────────────────────────────────────────────────

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

  // ✅ ONE-TIME SETUP
  await AutoPilotApi.init(
    baseUrl      : 'https://jsonplaceholder.typicode.com',
    enableLogs   : true,
    printPayload : true,   // full JSON in debug console
    prettyPrint  : true,   // pretty formatted
    enableCache  : true,
    cacheDuration: const Duration(minutes: 3),
    maxRetries   : 2,
    tokenType    : 'Bearer',

    enableGlobalLoader : true,
    onLoadingChanged   : (loading) =>
        debugPrint('⏳ Global loader: $loading'),

    onError : (msg, code) =>
        debugPrint('🔴 Global error [$code]: $msg'),

    onRequestSent      : (url, method) =>
        debugPrint('📤 $method → $url'),
    onResponseReceived : (url, code, time) =>
        debugPrint('📥 $code ← $url (${time.inMilliseconds}ms)'),

    // Match your API's response envelope
    successKey   : 'status',
    successValue : true,
    messageKey   : 'message',
    dataKey      : 'data',
  );

  runApp(const MyApp());
}

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

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

  @override
  Widget build(BuildContext context) => MaterialApp(
        title  : 'AutoPilot Demo',
        theme  : ThemeData(
          colorScheme  : ColorScheme.fromSeed(seedColor: Colors.indigo),
          useMaterial3 : true,
        ),
        home: const DemoPage(),
      );
}

// ─── Demo Page ────────────────────────────────────────────────────────────────

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

class _DemoPageState extends State<DemoPage> {
  final api    = AutoPilotApi.instance;
  String _out  = 'Press a button to test 🚀';
  bool _loading= false;

  // ── GET list ──────────────────────────────────────────────────────────────
  Future<void> _getUsers() async {
    setState(() => _loading = true);

    final res = await api.get<List<UserModel>>(
      endpoint : '/users',
      parser   : (json) => (json as List).map(UserModel.fromJson).toList(),
      useCache : true,
    );

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ ${res.data?.length} users\n\n'
            '${res.data?.take(3).map((u) => u.toString()).join('\n\n')}\n\n'
            '⏱ ${res.responseTime?.inMilliseconds}ms  [${res.requestId}]'
          : '❌ ${res.message}';
    });
  }

  // ── GET single ────────────────────────────────────────────────────────────
  Future<void> _getUser() async {
    setState(() => _loading = true);

    final res = await api.get<UserModel>(
      endpoint : '/users/1',
      parser   : UserModel.fromJson,
    );

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ User fetched!\n\n${res.data}\n\n'
            '⏱ ${res.responseTime?.inMilliseconds}ms'
          : '❌ ${res.message}';
    });
  }

  // ── POST ──────────────────────────────────────────────────────────────────
  Future<void> _createPost() async {
    setState(() => _loading = true);

    final res = await api.post<PostModel>(
      endpoint : '/posts',
      body     : {
        'title'  : 'AutoPilot is awesome!',
        'body'   : 'Zero boilerplate API calls in Flutter.',
        'userId' : 1,
      },
      parser: PostModel.fromJson,
    );

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ Post created!\n\n${res.data}\n\n'
            'Status: ${res.statusCode}\n'
            '⏱ ${res.responseTime?.inMilliseconds}ms'
          : '❌ ${res.message}';
    });
  }

  // ── PUT ───────────────────────────────────────────────────────────────────
  Future<void> _updatePost() async {
    setState(() => _loading = true);

    final res = await api.put<PostModel>(
      endpoint : '/posts/1',
      body     : {
        'title'  : 'Updated via AutoPilot',
        'body'   : 'Updated body.',
        'userId' : 1,
      },
      parser: PostModel.fromJson,
    );

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ Post updated!\n\n${res.data}'
          : '❌ ${res.message}';
    });
  }

  // ── DELETE ────────────────────────────────────────────────────────────────
  Future<void> _deletePost() async {
    setState(() => _loading = true);

    final res = await api.delete(endpoint: '/posts/1');

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ Post deleted!\nStatus: ${res.statusCode}'
          : '❌ ${res.message}';
    });
  }

  // ── Query params ──────────────────────────────────────────────────────────
  Future<void> _queryParams() async {
    setState(() => _loading = true);

    final res = await api.get<List<PostModel>>(
      endpoint    : '/posts',
      queryParams : {'userId': 1, '_limit': 3},
      parser      : (json) =>
          (json as List).map(PostModel.fromJson).toList(),
    );

    setState(() {
      _loading = false;
      _out = res.isSuccess
          ? '✅ Posts for userId=1 (limit 3)\n\n'
            '${res.data?.map((p) => '• ${p.title}').join('\n')}'
          : '❌ ${res.message}';
    });
  }

  // ── .handle() extension ───────────────────────────────────────────────────
  Future<void> _handleExt() async {
    setState(() => _loading = true);

    await api
        .get<UserModel>(endpoint: '/users/3', parser: UserModel.fromJson)
        .handle(
          onSuccess : (data, msg) =>
              setState(() => _out = '✅ .handle() extension!\n\n$data'),
          onFailure : (msg, code) =>
              setState(() => _out = '❌ Failed [$code]: $msg'),
        );

    setState(() => _loading = false);
  }

  // ── Cache + Deduplication demo ────────────────────────────────────────────
  Future<void> _cacheDedup() async {
    setState(() {
      _loading = true;
      _out = '🔄 Firing 3 identical requests simultaneously...\n'
             '(Should deduplicate to 1 real network call)';
    });

    final sw = Stopwatch()..start();

    final results = await Future.wait([
      api.get<UserModel>(endpoint: '/users/2',
          parser: UserModel.fromJson, useCache: true),
      api.get<UserModel>(endpoint: '/users/2',
          parser: UserModel.fromJson, useCache: true),
      api.get<UserModel>(endpoint: '/users/2',
          parser: UserModel.fromJson, useCache: true),
    ]);

    sw.stop();

    setState(() {
      _loading = false;
      final names = results.map((r) => r.data?.name ?? '?').toSet();
      _out = '✅ 3 calls → 1 network request!\n\n'
             'All got same user: $names\n\n'
             'Individual times: ${results.map((r) => '${r.responseTime?.inMilliseconds}ms').join(', ')}\n'
             'Total wall time: ${sw.elapsedMilliseconds}ms';
    });
  }

  // ─── UI ───────────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor : const Color(0xFF0A0E1A),
      appBar: AppBar(
        backgroundColor : const Color(0xFF1A1F2E),
        title: const Text(
          '🚀 AutoPilot API Demo',
          style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
        ),
        bottom: PreferredSize(
          preferredSize : const Size.fromHeight(20),
          child         : const Padding(
            padding : EdgeInsets.only(bottom: 6),
            child   : Text(
              'Only http + shared_preferences  •  No Dio',
              style: TextStyle(color: Colors.white38, fontSize: 11),
            ),
          ),
        ),
      ),
      body: Padding(
        padding : const EdgeInsets.all(14),
        child   : Column(
          children: [
            // buttons
            Wrap(
              spacing    : 8,
              runSpacing : 8,
              children   : [
                _btn('GET List',     Icons.people,        _getUsers,    Colors.green),
                _btn('GET Single',   Icons.person,        _getUser,     Colors.blue),
                _btn('POST',         Icons.add_circle,    _createPost,  Colors.purple),
                _btn('PUT',          Icons.edit,          _updatePost,  Colors.orange),
                _btn('DELETE',       Icons.delete,        _deletePost,  Colors.red),
                _btn('?query',       Icons.filter_list,   _queryParams, Colors.teal),
                _btn('.handle()',    Icons.extension,     _handleExt,   Colors.pink),
                _btn('Cache/Dedup', Icons.cached,         _cacheDedup,  Colors.amber),
              ],
            ),

            const SizedBox(height: 10),

            // loader
            if (_loading)
              LinearProgressIndicator(
                minHeight  : 2,
                valueColor : AlwaysStoppedAnimation<Color>(Colors.indigoAccent),
              )
            else
              const SizedBox(height: 2),

            const SizedBox(height: 10),

            // terminal output
            Expanded(
              child: Container(
                width      : double.infinity,
                padding    : const EdgeInsets.all(14),
                decoration : BoxDecoration(
                  color        : const Color(0xFF0D1117),
                  borderRadius : BorderRadius.circular(10),
                  border       : Border.all(color: Colors.white10),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(children: [
                      _dot(Colors.red),
                      const SizedBox(width: 5),
                      _dot(Colors.orange),
                      const SizedBox(width: 5),
                      _dot(Colors.green),
                      const SizedBox(width: 10),
                      const Text('autopilot_output',
                          style: TextStyle(
                              color: Colors.white24,
                              fontSize: 11,
                              fontFamily: 'monospace')),
                    ]),
                    const SizedBox(height: 10),
                    Expanded(
                      child: SingleChildScrollView(
                        child: Text(
                          _out,
                          style: const TextStyle(
                            color      : Colors.greenAccent,
                            fontFamily : 'monospace',
                            fontSize   : 12,
                            height     : 1.6,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 8),
            const Text(
              '👆 Check debug console for colored request/response logs',
              style     : TextStyle(color: Colors.white24, fontSize: 10),
              textAlign : TextAlign.center,
            ),
          ],
        ),
      ),
    );
  }

  Widget _btn(String label, IconData icon, VoidCallback? fn, Color c) =>
      ElevatedButton.icon(
        onPressed : _loading ? null : fn,
        icon      : Icon(icon, size: 13),
        label     : Text(label, style: const TextStyle(fontSize: 11)),
        style     : ElevatedButton.styleFrom(
          backgroundColor : c.withOpacity(0.12),
          foregroundColor : c,
          side            : BorderSide(color: c.withOpacity(0.35)),
          padding         : const EdgeInsets.symmetric(
              horizontal: 10, vertical: 7),
          minimumSize: const Size(0, 0),
        ),
      );

  Widget _dot(Color c) => Container(
        width      : 10,
        height     : 10,
        decoration : BoxDecoration(color: c, shape: BoxShape.circle),
      );
}
3
likes
120
points
200
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Zero-boilerplate smart API engine for Flutter. Only uses http + shared_preferences.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, http, shared_preferences

More

Packages that depend on autopilot_api