Flutter Chen Common

Pub Version License

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
  • Theme – Customizable theme with light/dark support

UI Components

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