flutterx_starter_kit 0.0.1
flutterx_starter_kit: ^0.0.1 copied to clipboard
A starter kit widget to make project more easily.
FlutterX Starter Kit #
A comprehensive starter kit to accelerate Flutter application development with ready-to-use components following best practices.
Features #
β Currently Available #
- π ApiClient - Powerful HTTP client with complete features:
- Singleton pattern for easy global access
- Support for all HTTP methods (GET, POST, PUT, PATCH, DELETE)
- Auto token management & refresh token mechanism
- Custom headers per request or globally
- Query parameters support
- Automatic JSON encoding/decoding
- Built-in error handling & custom exceptions
- Timeout configuration
- Environment-based base URL
π§ Coming Soon #
- π¨ UI Components - Widget library (Button, Card, Input, etc.)
- π± Responsive Utils - Helper for responsive design
- π Auth Helper - Authentication utilities
- π Logger - Advanced logging system
Installation #
Add this package to your pubspec.yaml:
dependencies:
flutterx_starter_kit: ^0.0.1
Or install via command line:
flutter pub add flutterx_starter_kit
Getting Started #
1. Initialize ApiClient #
Initialize ApiClient in main() before runApp():
import 'package:flutter/material.dart';
import 'package:flutterx_starter_kit/flutterx_starter_kit.dart';
void main() {
// Initialize API Client
ApiClient.init(ApiConfig(
baseUrl: const String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'http://10.0.2.2:8080',
),
));
runApp(MyApp());
}
2. Basic Usage #
import 'package:flutterx_starter_kit/flutterx_starter_kit.dart';
// GET request
final response = await ApiClient.I.get('/users');
print(response.data); // parsed JSON response
// POST request
final newUser = await ApiClient.I.post('/users', {
'name': 'John Doe',
'email': 'john@example.com',
});
// PUT request
final updated = await ApiClient.I.put('/users/1', {
'name': 'Jane Doe',
});
// DELETE request
await ApiClient.I.delete('/users/1');
Usage #
Advanced Configuration #
With Token Provider & Refresh Token
ApiClient.init(ApiConfig(
baseUrl: 'https://api.example.com',
timeout: Duration(seconds: 30),
// Provide access token for authenticated requests
tokenProvider: () async {
// Get token from secure storage, SharedPreferences, etc
final prefs = await SharedPreferences.getInstance();
return prefs.getString('access_token');
},
// Auto refresh token when 401
refreshToken: (oldToken) async {
try {
final response = await http.post(
Uri.parse('https://api.example.com/refresh'),
body: {'token': oldToken},
);
final data = jsonDecode(response.body);
// Save new token
final prefs = await SharedPreferences.getInstance();
await prefs.setString('access_token', data['access_token']);
return data['access_token'];
} catch (e) {
return null; // Return null if refresh failed
}
},
// Handle unauthorized (after refresh token failed)
onUnauthorized: (statusCode, body) async {
// Navigate to login screen, clear session, etc
print('Session expired. Please login again.');
},
// Add global headers
defaultHeaders: () async {
return {
'X-App-Version': '1.0.0',
'X-Platform': Platform.isAndroid ? 'android' : 'ios',
};
},
));
Custom HTTP Client (for logging/mocking)
class LoggingClient extends http.BaseClient {
final http.Client _inner = http.Client();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
print('${request.method} ${request.url}');
return _inner.send(request);
}
}
ApiClient.init(ApiConfig(
baseUrl: 'https://api.example.com',
httpClient: LoggingClient(),
));
Request Options #
Query Parameters
// Method 1: Using query parameter
final users = await ApiClient.I.get(
'/users',
query: {'page': 1, 'limit': 10, 'role': 'admin'},
);
// Method 2: Using ApiRequestOptions
final users = await ApiClient.I.get(
'/users',
opts: ApiRequestOptions(
query: {'page': 1, 'limit': 10},
),
);
Custom Headers per Request
final response = await ApiClient.I.get(
'/users',
opts: ApiRequestOptions(
headers: {
'X-Custom-Header': 'value',
'Accept-Language': 'en',
},
),
);
Skip Authentication
// For public endpoints
final response = await ApiClient.I.get(
'/public/data',
opts: ApiRequestOptions(skipAuth: true),
);
Override Base URL
// Use different base URL for specific request
final response = await ApiClient.I.get(
'/external-api/data',
opts: ApiRequestOptions(
baseUrl: 'https://external-api.com',
),
);
Error Handling #
try {
final response = await ApiClient.I.get('/users');
print('Success: ${response.data}');
} on ApiException catch (e) {
// HTTP errors (4xx, 5xx)
print('API Error: ${e.message}');
print('Status Code: ${e.statusCode}');
print('Response Data: ${e.data}');
} on NetworkException catch (e) {
// Network/connection errors
print('Network Error: ${e.message}');
} catch (e) {
// Other errors
print('Unexpected Error: $e');
}
Working with Response #
final response = await ApiClient.I.get('/users');
// Access response data
print(response.statusCode); // 200
print(response.data); // parsed JSON (Map or List)
print(response.headers); // response headers
// Type-safe parsing
if (response.data is List) {
final users = (response.data as List)
.map((json) => User.fromJson(json))
.toList();
}
Complete Example #
import 'package:flutter/material.dart';
import 'package:flutterx_starter_kit/flutterx_starter_kit.dart';
void main() {
ApiClient.init(ApiConfig(
baseUrl: 'https://jsonplaceholder.typicode.com',
timeout: Duration(seconds: 30),
));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: UserListScreen(),
);
}
}
class UserListScreen extends StatefulWidget {
@override
_UserListScreenState createState() => _UserListScreenState();
}
class _UserListScreenState extends State<UserListScreen> {
List<dynamic> users = [];
bool isLoading = false;
String? error;
@override
void initState() {
super.initState();
fetchUsers();
}
Future<void> fetchUsers() async {
setState(() {
isLoading = true;
error = null;
});
try {
final response = await ApiClient.I.get('/users');
setState(() {
users = response.data as List;
isLoading = false;
});
} on ApiException catch (e) {
setState(() {
error = 'Error: ${e.message}';
isLoading = false;
});
} on NetworkException catch (e) {
setState(() {
error = 'Network error: ${e.message}';
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Users')),
body: isLoading
? Center(child: CircularProgressIndicator())
: error != null
? Center(child: Text(error!))
: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user['name']),
subtitle: Text(user['email']),
);
},
),
);
}
}
API Reference #
ApiClient #
Singleton HTTP client for REST API communication.
Static Methods
init(ApiConfig config)- Initialize singleton instanceI- Get singleton instance
Instance Methods
get(String endpoint, {Map<String, dynamic>? query, ApiRequestOptions opts})- GET requestpost(String endpoint, dynamic body, {Map<String, dynamic>? query, ApiRequestOptions opts})- POST requestput(String endpoint, dynamic body, {Map<String, dynamic>? query, ApiRequestOptions opts})- PUT requestpatch(String endpoint, dynamic body, {Map<String, dynamic>? query, ApiRequestOptions opts})- PATCH requestdelete(String endpoint, {Map<String, dynamic>? query, ApiRequestOptions opts, dynamic body})- DELETE request
ApiConfig #
Configuration for ApiClient.
Properties:
baseUrl(String) - API base URL without trailing slashtimeout(Duration) - Request timeout, default 20 secondstokenProvider(TokenProvider?) - Function to get access tokenrefreshToken(RefreshToken?) - Function to refresh token on 401defaultHeaders(DefaultHeadersBuilder?) - Function for global headersonUnauthorized(OnUnauthorized?) - Callback when unauthorizedhttpClient(http.Client?) - Custom HTTP client
ApiRequestOptions #
Per-request options to override global configuration.
Properties:
baseUrl(String?) - Override base URL for this requestheaders(Map<String, String>?) - Additional/override headersskipAuth(bool) - Skip Authorization header, default falsequery(Map<String, dynamic>?) - Query parameters
ApiResponse<T> #
Response wrapper from HTTP request.
Properties:
statusCode(int) - HTTP status codedata(T?) - Parsed response body (JSON β Map/List)headers(Map<String, String>) - Response headers
Exceptions #
ApiException
Exception for HTTP errors (4xx, 5xx).
Properties:
statusCode(int?) - HTTP status codemessage(String) - Error messagedata(dynamic) - Response body
NetworkException
Exception for network/connection errors. Extends ApiException.
Platform Support #
| Android | iOS | MacOS | Web | Linux | Windows |
|---|---|---|---|---|---|
| β | β | β | β | β | β |
Requirements #
- Flutter:
>=1.17.0 - Dart:
>=3.9.0
Examples #
See the /example folder for complete usage examples of this package.
Additional Information #
Contributing #
Contributions are welcome! Please:
- Fork this repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Issues and Feedback #
If you find a bug or have a feature suggestion:
- π Report bugs
- π‘ Request features
- π¬ Ask questions
Roadmap #
- β UI Components (Button, Card, TextField, etc.)
- β Responsive utilities
- β State management integration
- β Form validation helpers
- β Internationalization (i18n) support
- β Theme management
- β Navigation utilities
- β Advanced logging system
- β Performance monitoring
Changelog #
See CHANGELOG.md for version history.
License #
This package is licensed under the MIT License.
Author #
Ramadhan Hadiatma
- GitHub: @ramadhanhadiatmaa
- Repository: flutterx_starter_kit
Support #
If this package helps you, consider:
- β Star the repository on GitHub
- π Like the package on pub.flutter-io.cn
- π’ Share with other Flutter developers
Made with β€οΈ by Ramadhan Hadiatma