flutter_chen_common 4.4.0
flutter_chen_common: ^4.4.0 copied to clipboard
The flutter common package designed to help developers swiftly build aesthetically pleasing and feature-rich mobile application interfaces.
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
- 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 #
- 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: 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(), // example
loadingWidget: CustomLoading(), // example
),
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 [ // 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