flutter_chen_common 4.0.1
flutter_chen_common: ^4.0.1 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 #
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