flutter_next_base 0.2.0
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.
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'),
),
],
),
),
),
],
),
);
}
}