A single Flutter package for common app foundations: networking, repository layer, secure storage, UI components, helpers, and theme utilities.
- Highlights
- Requirements
- Installation
- Quick Start
- Configuration
- Module Reference
- Widget Catalog
- Re-exports
- Notes
- Opinionated API/repository stack with Dio + Riverpod
- Secure token/session/theme persistence via
FlutterSecureStorage
- Reusable widgets for forms, lists, feedback, and overlays
- Rich utility layer (formatters, validators, encryption, navigation)
- Light/dark theme building blocks with overridable palettes
- Dart SDK:
^3.2.0
- Flutter:
>=1.17.0
dependencies:
ccci_utility: ^1.1.132
import 'package:ccci_utility/ccci_utility.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: '.env');
runApp(const MyApp());
}
final dio = ref.watch(dioInstanceProvider);
final client = ref.watch(dioClientProvider); // with DioInterceptor
class ProfilesApi extends BaseApi {
ProfilesApi(DioClient client) : super('/university/v1.0/profiles', client);
}
class ProfileRepository extends BaseRepository<ProfileModel> {
ProfileRepository(super.api, super.fromJson);
}
final rows = await repo.get(queryParameters: {'limit': 10});
CButton(
label: 'Save',
onPressed: () {},
);
CInput(
hintText: 'Email',
validator: (v) => isValidEmail(v ?? '') ? null : 'Invalid email',
);
DioClient resolves baseUrl in this order:
| Priority |
Source |
Key/Path |
| 1 |
Secure storage |
baseUrl via BaseStore().getBaseUrl() |
| 2 |
Env var |
.env key BASE_URL |
| 3 |
Asset fallback |
assets/config.json with current: true entry |
BASE_URL=https://api.example.com
{
"development": {
"current": true,
"baseUrl": "https://dev.example.com"
},
"production": {
"current": false,
"baseUrl": "https://api.example.com"
}
}
| Component |
Purpose |
Key APIs |
BaseApi |
Route-based HTTP wrapper |
getById, get, post, put, delete |
BaseRepository<T> |
Generic data mapping layer |
get, getById, post, put, getWithCount, postWithCount |
BaseResponseModel<T> |
Count + rows response model |
count, rows, isEmpty, isNotEmpty |
DioClient |
Low-level HTTP client wrapper |
get, post, put, delete |
DioInterceptor |
Auto bearer token header |
uses BaseStore().getToken() |
DioExceptions |
Human-friendly Dio errors |
DioExceptions.fromDioError(...) |
createErrorResponse |
Normalized error message helper |
from return_handling.dart |
dioInstanceProvider / dioClientProvider |
Riverpod providers |
Provider<Dio>, Provider<DioClient> |
| Component |
Purpose |
Key APIs |
BaseStore |
Secure storage abstraction |
setToken, getToken, hasExpired, isLoggedIn, clearStorage, theme methods |
baseStore |
Global store instance |
shared BaseStore() |
BaseServiceProvider |
Abstract service base for Riverpod |
BaseServiceProvider(ProviderRef ref) |
| Area |
Exports |
| Logging |
AppLogger.debug/info/warning/error/verbose and API log helpers |
| Navigation |
RouteHandler.push, replace, pop, popUntil, pushAndRemoveUntil |
| Encryption |
encryptData, decryptData, deriveKeyAndIV, genRandomWithNonZero |
| Date/Time |
formatDate, formatTime, dateRangeFormatter, relativeTimeFormatter, formatCountdown, weekOfYear |
| String/Text |
initialsFormatter, toTitleCase, toCamelCase, toSnakeCase, toKebabCase, truncateString, stripHtmlTags |
| Numeric |
formatCurrency, formatCompactCurrency, formatFileSize, formatOrdinal, formatPercentage |
| Validators |
isValidEmail, isValidUrl, hasOnlyNumbers, isValidCreditCard, isValidIPv4, validatePasswordStrength |
| Component |
Purpose |
Key APIs |
Geolocation |
Permission, location, reverse geocoding, stream |
checkPermission, getLocation, getISOCode, getAddress, streamListener, stopStreamListener |
LocalAuthService |
Device biometric auth wrapper |
isDeviceSupported, hasBiometrics, authenticate, cancelAuthentication |
Styles, Models, and Enums #
| Group |
Exports |
| Theme |
AppTheme.themeData, AppTheme.darkTheme, themesProvider, ThemesProvider |
| Colors/Typography |
AppColors, SemanticColors, AppText, TextStyle.theme(context, ...) |
| Models |
AttachmentMetadataTypeModel, MinioAttachmentTypeModel, MenuPage, DrawerMenu, FieldData |
| Enums |
LOADING_STATE, DateFormatType, TimeFormatType, TimeHourCycle |
| Widget/Function |
Purpose |
CAvatar |
Circular avatar with network/local image fallback |
CAvatarStack |
Overlapped avatar stack with overflow indicator |
CButton |
Configurable button with loading/icon/svg/reverse ordering |
CCard |
Styled container card with optional tap handling |
CImage |
Local or cached-network image renderer |
CInput |
Flexible TextFormField wrapper |
CLoader |
Lottie-based loading indicator |
CLoaderDialog(...) |
Imperative loading dialog helper |
CReadMoreText |
Expandable/collapsible rich text with annotations |
CRefresh |
Pull-to-refresh wrapper with custom indicator |
CTile |
Reusable list tile with optional avatar/actions |
CError |
Centered error UI block |
CSliverAppBarDelegate |
Persistent sliver header delegate |
CBottomSheet(...) |
Themed modal bottom sheet helper |
CConfirmationDialog(...) |
Platform-adaptive confirmation dialog |
ccci_utility.dart re-exports commonly used dependencies:
dio
flutter_riverpod
go_router
flutter_dotenv
freezed_annotation
jwt_decoder
flutter_lucide
geocoding
geolocator
syncfusion_flutter_maps
web_socket_channel
flutter/services.dart
DioClient does not load .env automatically; call dotenv.load(...) in app startup.
- If using fallback config, ensure your app includes
assets/config.json in assets.
DioInterceptor expects token from secure storage key used by BaseStore.