Flutter Chen Common
Overview
English | 中文
Flutter Chen Common is a feature-rich Flutter utility library that provides a one-stop solution for app development.
Customizable theme system
Enterprise-grade networking
Enterprise-grade logging
High-quality common widgets
Handy utilities and extensions
Smart refresh list solution
Ready-to-use common popups
Unified multi-state layout
Context-free global Toast
Features
🎨 Theme system: configure colors, corners, spacing via ThemeExtension
🌍 Internationalization: recommend official Flutter localization (built-in module deprecated)
⚡ Priority overrides: global configuration + per-component overrides
📱 Adaptive design: perfect fit for iOS/Material guidelines
🔥 Enterprise-grade modules: logging/networking/security out of the box
Documentation
Core
Networking – Structured logs, retries, token refresh
Logging – File logging, filters, custom outputs
Network Debug - The network request debugging module provides lightweight packet capture functionality, visual logs, and more.
Theme – Customizable theme with light/dark support
UI Components
Quick Start
Install
Add dependency in pubspec.yaml:
dependencies:
flutter_chen_common: latest version
Run:
flutter pub get
Initialization
final navigatorKey = GlobalKey <NavigatorState >();
void main () async {
WidgetsFlutterBinding .ensureInitialized ();
await SpUtil .init ();
Directory dir = await getApplicationDocumentsDirectory ();
await Log .init (
LogConfig (
retentionDays: 3 ,
enableFileLog: true ,
logLevel: LogLevel .all,
recordLevel: LogLevel .info,
output: const [],
printer: PrettyPrinter (dateTimeFormat: DateTimeFormat .dateAndTime),
logDirectory: kIsWeb ? null : Directory ('${dir.path} /logs' ),
),
);
HttpClient .init (
config: HttpConfig (
baseUrl: 'https://api.example.com' ,
connectTimeout: const Duration (seconds: 30 ),
receiveTimeout: const Duration (seconds: 30 ),
sendTimeout: const Duration (seconds: 30 ),
commonHeaders: () => {"Authorization" : SpUtil .getString ('token' )},
interceptors: [CustomInterceptor ()]
enableLog: true ,
enableToken: true ,
maxRetries: 3 ,
getToken: () => "token" ,
onRefreshToken: () async {
return "new_token" ;
},
onRefreshTokenFailed: () async {
Log .d ("Log in again" );
},
),
);
ComContext .init (navigatorKey);
ComToast .init (
config: const ComToastConfig (
duration: Duration (seconds: 2 ),
position: ComToastPosition .center,
),
builder: ComToastWidgetBuilder (
success: const ComToastConfig (
iconWidget:
Icon (Icons .check_circle, color: Color (0xFF10B981 ), size: 20 ),
),
error: const ComToastConfig (
iconWidget: Icon (Icons .cancel, color: Color (0xFFEF4444 ), size: 20 ),
),
warning: const ComToastConfig (
iconWidget:
Icon (Icons .priority_high, color: Color (0xFFF59E0B ), size: 20 ),
),
info: const ComToastConfig (
iconWidget:
Icon (Icons .info_outline, color: Color (0xFF3B82F6 ), size: 20 ),
),
loading: ComToastConfig (
builder: (ctx) => const ComContainer (
width: 120 ,
child: ComLoading (),
),
clickThrough: false ,
duration: Duration .zero,
),
),
);
await ComDebugManager .instance.init (
dio: HttpClient .instance.dio,
comDebugConfig: ComDebugConfig (
enabled: Env .env != EnvEnum .prod,
maxLogCount: 100 ,
sanitize: (log) {
if (log.request?.headers.containsKey ('Authorization' ) ?? false ) {
log.request?.headers['Authorization' ] = '***' ;
}
return log;
},
));
runApp (MyApp ());
}
class MyApp extends StatelessWidget {
@override
Widget build (BuildContext context) {
return ComConfiguration (
config: ComConfig .defaults ().copyWith (
emptyWidget: CustomEmptyWidget (),
loadingWidget: CustomLoading (),
),
child: MaterialApp (
navigatorKey: navigatorKey,
builder: (context, child) {
ComNetworkLogger .show (context);
child = ComToastBuilder ()(context, child);
return child;
},
navigatorObservers: [ComToastNavigatorObserver ()],
theme: ThemeData .light ().copyWith (
extensions: [ComTheme .light ()],
),
darkTheme: ThemeData .dark ().copyWith (
extensions: [ComTheme .dark ()],
),
home: MainPage (),
localizationsDelegates: const [
GlobalMaterialLocalizations .delegate,
GlobalWidgetsLocalizations .delegate,
GlobalCupertinoLocalizations .delegate,
],
supportedLocales: const [
Locale ('zh' , 'CN' ),
Locale ('en' , 'US' ),
],
),
);
}
}
🌐 Networking
HttpClient .instance.request (
"/api" ,
method: HttpMethod .post,
fromJson: (json) => User .fromJson (json),
showLoading: true ,
)
HttpClient .instance.get ("/api" );
HttpClient .instance.post ("/api" );
HttpClient .instance.put ("/api" );
HttpClient .instance.patch ("/api" );
HttpClient .instance.delete ("/api" );
HttpClient .instance.uploadFile ("/api" ,"filePath" );
HttpClient .instance.downloadFile ("/api" , "savePath" );
┌─────────────────────────────────────────────────────────────────────────────
│ ✅ [HTTP] 2025 -04 -05 23 :30 :29 Request sent [Duration ] 88ms
│ Request : 200 GET http:
│ Headers : {"token" :"xxxxx" ,"content-type" :"application/json" }
│ Query : {"xxxx" :"xxxx" }
│ Response : {"weatherinfo" :{"city" :"北京" ,"cityid" :"101010100" ,"WD" :"东南风" }}
└──────────────────────────────────────────────────────────────────────────────
📝 Logging
Log .d ("debug message" );
Log .i ("info message" );
Log .w ("warning message" );
Log .e ("error message" );
Log .console ("console message printed fully without prefix" );
final Directory dir = await Log .getLogDir ();
class LogConfig {
final int retentionDays;
final bool enableFileLog;
final LogLevel logLevel;
final LogLevel recordLevel;
final List <LogOutput >? output;
final Directory ? logDirectory;
const LogConfig ({
this .retentionDays = 3 ,
this .enableFileLog = true ,
this .logLevel = LogLevel .all,
this .recordLevel = LogLevel .info,
this .output,
this .logDirectory,
});
}
class SentryOutput extends LogOutput {
@override
void output (OutputEvent event) {
if (event.level.value >= LogLevel .error.value) {
Sentry .captureException (
event.error,
stackTrace: event.stackTrace,
tags: {'log_level' : event.level.name},
);
}
}
}
Log .init (LogConfig (
output: [SentryOutput ()]
));
Utils
File
Description
clipboard_util.dart
Clipboard
file_util.dart
File operations
keyboard_util.dart
Keyboard helpers
platform_util.dart
Platform detection
platform_util_web.dart
Web platform helpers
sp_util.dart
Local storage
utils.dart
Utils entry
Widgets
Basic
File
Description
base_widget.dart
Base (multi-state)
com_loading.dart
Loading widget
Functional
File
Description
com_marquee.dart
Marquee
com_popup_menu.dart
Popup menu
Refresh
File
Description
back_top_widget.dart
Back to top
refresh_controller.dart
Refresh controller
refresh_interface.dart
Refresh interface
refresh_state.dart
Refresh state
refresh_strategy.dart
Refresh strategy
refresh_widget.dart
Smart refresh
Theme
Built-in
Name
Example
Light Theme
ComTheme.light
Dark Theme
ComTheme.dark
Configurable
ComTheme (
shapes: ComShapes .standard,
spacing: ComSpacing .standard,
success: Colors .green.shade600,
error: Colors .red.shade600,
warning: Colors .orange.shade600,
link: Colors .blue.shade600,
)
Internationalization
Built-in i18n module is deprecated. Use Flutter official flutter_localizations + intl.
Global State Layout
ComConfiguration (
config: ComConfig .defaults ().copyWith (
emptyWidget: const ComLoading (),
loadingWidget: const ComEmpty (),
errorWidget: const ComErrorWidget (),
noNetworkWidget: (VoidCallback ? onReconnect) => ComNoNetworkWidget (onReconnect: onReconnect),
),
child: child,
);
BaseWidget (
isConnected: isConnected,
status: LayoutStatus .loading,
loading: const ComLoading (),
empty: const CustomEmpty (),
error: BaseWidget .errorWidget (context),
noNetwork: BaseWidget .noNetworkWidget (context),
onReconnect: (){},
child: child,
)
enum LayoutStatus {
loading,
success,
empty,
error,
noNetwork,
}
BaseWidget .loadingWidget (context)
BaseWidget .emptyWidget (context)
BaseWidget .errorWidget (context)
BaseWidget .noNetworkWidget (context)
...
Contributing
We welcome all contributions:
🐛 Bug reports
💡 Feature ideas
📚 Docs improvements
🎨 Design assets
💻 Code contributions
License
MIT License – see LICENSE