Flutter Chen Common
Overview
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
- Theme β Customizable theme with light/dark support
UI Components
- Smart Refresh β Refresh list solution
- Toast β Global Toast without BuildContext
- Marquee β Any widget marquee
- PopupMenu β Beautiful popup menus
Online Demo
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();
// shared_preferences
await SpUtil.init();
// Logging
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'),
),
);
// Networking
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");
},
),
);
// Global context for overlays
ComContext.init(navigatorKey);
// Global Toast config (optional)
ComToast.init(
config: ComToastConfig(
duration: const Duration(seconds: 2),
position: ComToastPosition.center,
),
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ComConfiguration(
config: ComConfig.defaults().copyWith(
emptyWidget: CustomEmptyWidget(), // example
loadingWidget: CustomLoading(), // example
),
child: MaterialApp(
navigatorKey: navigatorKey,
builder: ComToastBuilder(),
navigatorObservers: [ComToastNavigatorObserver()],
theme: ThemeData.light().copyWith(
extensions: [ComTheme.light()],
),
darkTheme: ThemeData.dark().copyWith(
extensions: [ComTheme.dark()],
),
home: MainPage(),
localizationsDelegates: const [ // official localization
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");
// HttpConfig key fields:
// baseUrl, connectTimeout, receiveTimeout, sendTimeout,
// commonHeaders(): Map<String, dynamic>,
// interceptors: List<Interceptor>,
// enableLog, enableToken, enableRetry,
// maxRetries, retriesDelay,
// getToken(), onRefreshToken(), onRefreshTokenFailed()
// Sample log output
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
[HTTP] 2025-04-05 23:30:29 Request sent [Duration] 88ms
β Request: 200 GET http://www.weather.com.cn/data/sk/101010100.html?xxxx=xxxx
β 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
// Global or local configuration
ComConfiguration(
config: ComConfig.defaults().copyWith(
emptyWidget: const ComLoading(), // example only
loadingWidget: const ComEmpty(), // example only
errorWidget: const ComErrorWidget(), // example only
noNetworkWidget: (VoidCallback? onReconnect) => ComNoNetworkWidget(onReconnect: onReconnect), // example only
),
child: child,
);
// BaseWidget states use global config by default; can be customized locally
BaseWidget(
isConnected: isConnected,
status: LayoutStatus.loading,
loading: const ComLoading(),
empty: const CustomEmpty(),
error: BaseWidget.errorWidget(context),
noNetwork: BaseWidget.noNetworkWidget(context),
onReconnect: (){},
child: child,
)
// State types
enum LayoutStatus {
loading,
success,
empty,
error,
noNetwork,
}
// Global helpers
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
Libraries
- config/com_config
- config/com_configuration
- config/com_context
- config/config
- extension/context_extension
- extension/extension
- flutter_chen_common
- log/filter/log_filter
- log/log
- log/log_config
- log/log_level
- log/logger
- log/output/isolate_file_output
- network/http_client
- network/http_config
- network/http_response
- network/interceptors/log_interceptor
- network/interceptors/retry_interceptor
- network/interceptors/token_interceptor
- network/network
- theme/com_shape
- theme/com_spacing
- theme/com_theme
- theme/theme
- toast/src/loading_manager
- toast/src/loading_widget
- toast/src/overlay_manager
- toast/src/overlay_observer
- toast/src/toast
- toast/src/toast_config
- toast/src/toast_manager
- toast/src/toast_widget
- toast/toast
- utils/clipboard_util
- utils/file_util
- utils/keyboard_util
- utils/platform_util
- utils/platform_util_web
- utils/sp_util
- utils/utils
- widgets/base/base_widget
- widgets/base/com_loading
- widgets/marquee/com_marquee
- widgets/refresh/back_top_widget
- widgets/refresh/refresh_controller
- widgets/refresh/refresh_interface
- widgets/refresh/refresh_state
- widgets/refresh/refresh_strategy
- widgets/refresh/refresh_widget
- widgets/widgets