result_utils 0.0.1
result_utils: ^0.0.1 copied to clipboard
A Flutter package providing a Result pattern for error handling using sealed classes (Success/Error).
import 'package:flutter/material.dart';
import 'package:result_utils/result_utils.dart';
void main() {
runApp(const ResultUtilsExampleApp());
}
class ResultUtilsExampleApp extends StatelessWidget {
const ResultUtilsExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Result Utils Example',
theme: ThemeData(useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)),
home: const ExamplePage(),
);
}
}
class ExamplePage extends StatefulWidget {
const ExamplePage({super.key});
@override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final TextEditingController _dividendController = TextEditingController();
final TextEditingController _divisorController = TextEditingController();
String _result = 'Enter two numbers to divide';
/// Example 1: Simple sync function returning FutureResult
FutureResult<int> divide(int a, int b) {
if (b == 0) {
return FutureResult.error('Cannot divide by zero');
}
return FutureResult.success(a ~/ b);
}
/// Example 2: Async function wrapped with futureToResult
Future<FutureResult<String>> fetchUserData(String userId) async {
return futureToResult(
Future.delayed(const Duration(seconds: 1), () {
if (userId.isEmpty) {
throw Exception('User ID cannot be empty');
}
return 'User $userId loaded successfully!';
}),
);
}
void _handleDivision() {
final dividend = int.tryParse(_dividendController.text);
final divisor = int.tryParse(_divisorController.text);
if (dividend == null || divisor == null) {
setState(() {
_result = 'Error: Please enter valid numbers';
});
return;
}
final result = divide(dividend, divisor);
setState(() {
if (result.hasError) {
_result = 'Error: ${result.error}';
} else {
_result = '$dividend ÷ $divisor = ${result.value}';
}
});
}
Future<void> _handleAsyncFetch() async {
setState(() {
_result = 'Loading...';
});
final result = await fetchUserData('user123');
setState(() {
if (result.hasError) {
_result = 'Error: ${result.error}';
} else {
_result = result.value ?? 'Unknown result';
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Result Utils Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Example 1: Sync Result Pattern
const Text(
'Example 1: Simple Division (Sync)',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
TextField(
controller: _dividendController,
decoration: const InputDecoration(labelText: 'Dividend', border: OutlineInputBorder()),
keyboardType: TextInputType.number,
),
const SizedBox(height: 12),
TextField(
controller: _divisorController,
decoration: const InputDecoration(labelText: 'Divisor', border: OutlineInputBorder()),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
ElevatedButton(onPressed: _handleDivision, child: const Text('Divide')),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Text(_result, style: const TextStyle(fontSize: 16)),
),
const SizedBox(height: 32),
// Example 2: Async Result Pattern
const Text(
'Example 2: Async Operation (with futureToResult)',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
ElevatedButton(onPressed: _handleAsyncFetch, child: const Text('Fetch User Data')),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Text(_result, style: const TextStyle(fontSize: 16)),
),
const SizedBox(height: 32),
// Info Box
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue),
),
child: const Text(
'The Result pattern replaces try-catch with type-safe Success/Error handling. '
'No exceptions are thrown; instead, functions return FutureResult<T> that is '
'either Success or Error. This makes error handling explicit and compile-time safe.',
style: TextStyle(fontSize: 14),
),
),
],
),
),
),
);
}
@override
void dispose() {
_dividendController.dispose();
_divisorController.dispose();
super.dispose();
}
}