flutter_next_base 0.2.0 copy "flutter_next_base: ^0.2.0" to clipboard
flutter_next_base: ^0.2.0 copied to clipboard

A Frappe/ERPNext REST client for Flutter. Resource CRUD, submit and cancel, permission checks, link search, bulk writes, file upload and typed error handling.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_next_base/flutter_next_base.dart';
import 'package:logging/logging.dart';

void main() {
  // Setup logging
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    debugPrint('${record.level.name}: ${record.time}: ${record.message}');
  });

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Next Base Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const ExampleScreen(),
    );
  }
}

class ExampleScreen extends StatefulWidget {
  const ExampleScreen({super.key});

  @override
  State<ExampleScreen> createState() => _ExampleScreenState();
}

class _ExampleScreenState extends State<ExampleScreen> {
  late FlutterNextBaseClient client;
  String output = 'Ready to make API calls...';
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    // Initialize client with your Frappe site URL
    client = FlutterNextBaseClient(
      baseUrl: 'https://your-frappe-site.com',
      // Add your cookie manager here if needed
    );
  }

  @override
  void dispose() {
    client.dispose();
    super.dispose();
  }

  void _setOutput(String text) {
    setState(() {
      output = text;
      isLoading = false;
    });
  }

  void _setLoading() {
    setState(() {
      isLoading = true;
      output = 'Loading...';
    });
  }

  Future<void> _getResourceList() async {
    _setLoading();
    
    final result = await client.getResourceList(
      'Delivery Request',
      options: const QueryOptions(
        filters: '[["status", "=", "Open"]]',
        fields: ['name', 'customer', 'status', 'delivery_date'],
        orderBy: 'creation desc',
        limitPageLength: 10,
      ),
    );

    if (result.isSuccess) {
      final data = result.data!['data'] as List;
      _setOutput('Found ${data.length} delivery requests:\n\n'
          '${data.map((d) => '${d['name']} - ${d['customer']}').join('\n')}');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _getResource() async {
    _setLoading();
    
    final result = await client.getResource('Delivery Request', 'DR-00001');

    if (result.isSuccess) {
      final doc = result.data!['data'];
      _setOutput('Resource Details:\n\n'
          'Name: ${doc['name']}\n'
          'Customer: ${doc['customer']}\n'
          'Status: ${doc['status']}\n'
          'Date: ${doc['delivery_date']}');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _createResource() async {
    _setLoading();
    
    final result = await client.createResource('Delivery Request', {
      'customer': 'CUST-00001',
      'delivery_date': DateTime.now().add(const Duration(days: 1)).toIso8601String().split('T')[0],
      'status': 'Open',
    });

    if (result.isSuccess) {
      final doc = result.data!['data'];
      _setOutput('Created Successfully!\n\n'
          'Name: ${doc['name']}\n'
          'Customer: ${doc['customer']}');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _updateResource() async {
    _setLoading();
    
    final result = await client.updateResource(
      'Delivery Request',
      'DR-00001',
      {'status': 'In Transit'},
    );

    if (result.isSuccess) {
      _setOutput('Updated Successfully!\n\nStatus changed to: In Transit');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _getDoc() async {
    _setLoading();
    
    final result = await client.getDoc('Delivery Trip', 'DT-00001');

    if (result.isSuccess) {
      final doc = result.data!['docs'][0];
      _setOutput('Document Details:\n\n'
          'Name: ${doc['name']}\n'
          'Status: ${doc['status']}\n'
          'Driver: ${doc['driver']}\n'
          'Stops: ${doc['stops']?.length ?? 0}');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _setValue() async {
    _setLoading();
    
    final result = await client.setValue(
      'Delivery Request',
      'DR-00001',
      'status',
      'Completed',
    );

    if (result.isSuccess) {
      _setOutput('Value Set Successfully!\n\nStatus updated to: Completed');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  Future<void> _callCustomMethod() async {
    _setLoading();
    
    final result = await client.callMethod(
      'delivery.api.update_status',
      data: {
        'delivery_request': 'DR-00001',
        'status': 'In Transit',
        'notes': 'Out for delivery',
      },
    );

    if (result.isSuccess) {
      _setOutput('Custom Method Called Successfully!\n\n'
          'Response: ${result.data!['message']}');
    } else {
      _setOutput('Error: ${result.error!.message}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Next Base Example'),
        elevation: 2,
      ),
      body: Column(
        children: [
          Expanded(
            flex: 2,
            child: Container(
              width: double.infinity,
              padding: const EdgeInsets.all(16),
              color: Colors.grey[100],
              child: SingleChildScrollView(
                child: Text(
                  output,
                  style: const TextStyle(
                    fontFamily: 'monospace',
                    fontSize: 14,
                  ),
                ),
              ),
            ),
          ),
          if (isLoading)
            const LinearProgressIndicator(),
          Expanded(
            flex: 3,
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  const Text(
                    'Resource Operations',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 12),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _getResourceList,
                    icon: const Icon(Icons.list),
                    label: const Text('Get Resource List'),
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _getResource,
                    icon: const Icon(Icons.article),
                    label: const Text('Get Single Resource'),
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _createResource,
                    icon: const Icon(Icons.add),
                    label: const Text('Create Resource'),
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _updateResource,
                    icon: const Icon(Icons.edit),
                    label: const Text('Update Resource'),
                  ),
                  const Divider(height: 32),
                  const Text(
                    'DocType Operations',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 12),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _getDoc,
                    icon: const Icon(Icons.description),
                    label: const Text('Get Doc (Full Details)'),
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _setValue,
                    icon: const Icon(Icons.edit_note),
                    label: const Text('Set Field Value'),
                  ),
                  const Divider(height: 32),
                  const Text(
                    'Custom Methods',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 12),
                  ElevatedButton.icon(
                    onPressed: isLoading ? null : _callCustomMethod,
                    icon: const Icon(Icons.api),
                    label: const Text('Call Custom Method'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
160
points
82
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Frappe/ERPNext REST client for Flutter. Resource CRUD, submit and cancel, permission checks, link search, bulk writes, file upload and typed error handling.

Repository (GitHub)
View/report issues

Topics

#frappe #erpnext #rest-client #api #doctype

License

MIT (license)

Dependencies

flutter, http, logging

More

Packages that depend on flutter_next_base