Slim은 로컬라이제이션 지원, UI 메시지 관리, 상태 관리 유틸리티, 유용한 확장 기능 및 REST API 통합을 포함하는 필수 앱 인프라를 제공하는 Flutter 패키지입니다. 일반적인 개발 작업을 간소화하도록 설계되어 확장 가능하고 유지보수가 쉬운 애플리케이션을 구축하기 위한 일관된 기반을 제공합니다. 단순성과 재사용성을 중시하며 프로젝트 전반에서 반복 코드를 줄입니다. 모듈식 구조 덕분에 개발자는 필요에 따라 필요한 구성 요소만 사용할 수 있습니다. 특히 앱 아키텍처를 표준화하고 개발 속도를 높이고자 하는 팀에게 매우 유용합니다.
사용 사례
앱 콘텐츠 로컬라이징
UI 메시지 관리
상태 관리 간소화
REST API 클라이언트 추가
재사용 가능한 UI 확장 만들기
앱 아키텍처 표준화
주요 기능
내장된 로컬라이제이션 지원
UI 메시지 시스템
상태 관리 도우미
REST API 통합
유용한 Dart 확장
모듈러 디자인
적합한 대상
플러터 초보자
팀 기반 앱 개발
대규모 플러터 앱
빠른 프로토타이핑
현지화가 필요한 앱
자주 API 상호작용이 있는 프로젝트
고려 사항
제한된 플랫폼 고유 기능
내장 테마 없음
복잡한 상태를 위해 수동 설정이 필요합니다
매우 전문적인 UI 요구 사항에는 적합하지 않습니다
완전한 기능을 위해 외부 종속성에 의존합니다
고급 사용을 위한 설명서는 최소한일 수 있습니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
slim - app essentials
slim was written to give some app essentials and capabilities that are common for most apps.
slim makes it easier to set up app infrastructure so you can start working on your screens and logic.
Localizations
UI Messages
State Management
AppLifecycleState Events
Useful Extensions
Rest Api
Configurations
Easy app level configuration that gives 'ready to use' localization and UI messages.
App class constructor
This is the place to set your supported locales and if you want also set your own SlimLocaleLoader
The default SlimLocaleLoader will load you locale files from - assets/locales/ so make sure you created folder and files matching your supported locales
and add it to pubspec.yaml file.
MaterialApp builder
Add the SlimMaterialAppBuilder.builder to it. If you have additional builders just chain them.
SlimMaterialAppBuilder.builder gives you the UI messages caps.
MaterialApp localizationsDelegates
Set to SlimLocalizations.delegates
That will support all delegates needed including the SlimLocalizations.slimLocaleLoader delegate.
MaterialApp supportedLocales
Set to SlimLocalizations.supportedLocales you configured in App class constructor.
The following example will load simple json locale configurations from - assets/locales/en.json
import'package:slim/slim.dart';
classMyAppextendsStatelessWidget{
MyApp(){
SlimLocalizations.supportedLocales = [Locale('en', 'US')];
/// If you want to customize you locale loader just create class that extends SlimLocaleLoader and change:////// SlimLocalizations.slimLocaleLoader= YouCustomLocalLoader();
}
@overrideWidgetbuild(BuildContext context) =>
MaterialApp(
builder: SlimMaterialAppBuilder.builder,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage("First Screen"),
localizationsDelegates: SlimLocalizations.delegates,
supportedLocales: SlimLocalizations.supportedLocales,
);
}
Localization
UI direction
The confgurations above will make your UI ltr/rtl automatically according to os locale.
You can use BuildContext extension method to get your locale text direction context.textDirection
Translation
If you didn't provide any custom SlimLocaleLoader , Your localization would be configured with a default one. The default SlimLocaleLoader expects locale files to be in - assets/locales/ folder with name convention of your locale. for example - assets/locales/en.json.
The default locale file format is a single level json that holds you translation. You can extends SlimLocaleLoader to your own.
{"welcome":"translation of 'welcome' key"}
Access to translation of a key gained by BuildContext extension method called translate.
UI Messages caps are available via BuildContext extension methods. Since SlimController accessed by SlimBuilder has an access to current context, these caps are also available inside it. That will be explained in the State Management section below.
You can display overlay with your own widget, text message and a snackbar.
For text overlay and snackbar you can set the background color and text style.
For overlay of text or widget you can specify if dismissible.
State Management
slim provides state management based on InhertiedNotifier and ChangeNotifier classes.
The concept is that you keep an object at the top of the tree or sub tree of children that might access it.
The state object can be a widget state object, data object, business object or whatever you want it to be.
You can even use it for passing parameters between screens, so it can be a String, int or even an enum.
This is the one of the powerful concepts of slim state management - it can be any state and not limited for a widget state.
Before you go over below examples, excluding the case of parameter passing between screens, the most recommended state object is the SlimController and recommended way to access it is via SlimBuilder widget.
By using the SlimController from one side and SlimBuilder on the other, you get the best of three worlds:
Widget state control - you can make the wrapped widget to rebuild by calling updateUI method.
Since SlimController based on ChangeNotifier it will cause all widgets that referenced it to rebuild.
That is great but considered to be a ChangeNotifier disadvantage in case you only want to rebuild the current widget. For that you can pass the current flag and decide when you want to globaly or localy update the UI.
updateUI({bool current = false})
Global & Local shared objects/values - you can make any object/values shared between your app screens.
Global - put your object/value at the top of your app tree by wrapping the material app widget.
Local - put your object/value at the top of a sub tree. That way you can share objects for a local tree or pass
parameters between screens.
Separate & not disconnected business logic - The SlimController accessed by SlimBuilder allows you to separate the business from UI but gives access to current context. That gives you that ability to combine navigation flows and use the UI messages inside your business class.
SlimController
abstract class that can be used for state management or logic, inherits from ChangeNotifier and gives you widget rebuild options: T slim<slim>() - access to ancestor slims
updateUI({bool current = false}) - will refresh the state of all / current widgets that reference it (current update flag workd only if you access it via [SlimBuilder] widget). closeKeyBoard() - close keyboard by requesting focuse
The SlimController has context propery to access the current context so you can use context extensions from inside a business login class interacting with UI: showOverlay - display overlay text message showWidget - display overlay widget showSnackBar - display snackbar with given text clearOverlay - clears overlays forceClearOverlay - clears overlays even if not dismissible
For overlay message and snackbar you can set background color, text style, overlay color and overlay opacity.
For overlay message and widget you can set dismissible flag.
SlimAppStateController
abstract class that inherits from SlimController and recieves AppLifecycleState events. the events change recieves only if the SlimAppStateController
is accessed in the current app's screen. SlimAppStateController force to override its void onAppStateChanged(AppLifecycleState state) method.
SlimAppStateController must be access via SlimBuilder, SlimController access via SlimBuilder is optional but recommended
Putting Slims in the tree
For the following example we will use a simple Counter class:
classCounterextendsSlimController{
int value=0;
inc(){
value++;
}
}
@overrideWidgetbuild(BuildContext context){
returnSlimBuilder<Counter>(
[instance: optional local instance of Counterif you dont want to pre put it on tree]
builder:(counter){
...
return someWidget;
}
);
}
Simple Access - without updateUI(current:true) support
@overrideWidgetbuild(BuildContext context){
final counter = Slim.of<Counter>(context);
...
return someWidget;
}
@overrideWidgetbuild(BuildContext context){
final counter = context.slim<Counter>();
...
return someWidget;
}
Useful Extensions
slim provides some useful extension methods for several classes (some of them mentioned previously).
The full extension methods list is:
BuildContext bool hasOverlay - true if any overlay currently displayed void clearOverlay() - clears current overlay message if dismissible void forceClearOverlay() - clears current overlay message even if not dismissible void showWidget(Widget widget, {bool dismissible = true})
T slim<T>() - access a state object of type T double width - media query width double height - media query height NavigatorState navigator - navigator state void pop<T>({T result}) - navigator pop Future<T> push<T>(Route<T> route) - navigator push Future<T> pushReplacement<T>(Route<T> route) - navigator pushReplacement void popTop() - navigator pop till can't pop anymore String translate(String key,[String group]) - locale translation of key TextDirection textDirection - current locale text direction void closeKeyboard() - request focuse
Widget Future<T> push<T>(BuildContext context) - navigator push Future<T> pushReplacement<T>(BuildContext context) - navigator pushReplacement Future<T> pushTop<T>(BuildContext context) - push at navigators most top
int Duration get seconds - get in seconds Duration get hours - get in hours Duration get days - get in days Duration get minutes - get in minutes Duration get milliseconds - get in milliseconds Duration get microseconds - get in microseconds DateTime get nowMinutesInterval - get current time with minutes round up to interval
RestApi
SlimApi is an abstract class that gives you rest (get, delete, post, put) methods for fast service writing.
The SlimApi class constructor gets the server url, and its methods gets the service url and some additional data. SlimApi class has a createHeaders method that can be overriden. SlimApi methods wrapped in try/catch clause and returns SlimResponse object.
/// Extends slim SlimController classclassLoginControllerextendsSlimController{
badLogin(User user) async {
/// Access login service via slimfinal loginService = slim<LoginService>();
/// Using context access to display loading indicatorshowWidget(CircularProgressIndicator(), dismissible:false);
final result = await loginService.login(user);
/// Using context access to clear loading indicatorforceClearOverlay();
/// Checking slim RestApiResult for successif (result.success)
/// Using slim widget extension method to replace current screen to Home widgetHome().pushReplacement(context);
else/// Using context access to show a snackbar and locale translationshowSnackBar(context.translate("badcreds"), backgroundColor: Colors.red);
}
goodLogin(User user) async {
/// Access login service via slimfinal loginService = slim<LoginService>();
/// Close keyboardcloseKeyboard();
/// Using context access to display loading indicatorshowWidget(CircularProgressIndicator(), dismissible:false);
await loginService.login(user);
//Using context access to clear loading indicatorforceClearOverlay();
/// Using slim widget extension method to replace current screen to Home widgetHome().pushReplacement(context);
}
}
App Configurations
classMyAppextendsStatelessWidget{
@overrideWidgetbuild(BuildContext context) {
return [
/// Putting single instance of Userat the top of the treeSlimer<User>(User()),
/// Putting single instance of LoginService at the top of the treeSlimer<LoginService>(LoginService()),
/// Putting single instance of LoginController at the top of the treeSlimer<LoginBloc>(LoginController()),
].slim(
child: MaterialApp(
/// Configure material app builder for slim UI messages support
builder: SlimMaterialAppBuilder.builder,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Login(),
/// Configure slim localizations delegates
localizationsDelegates: SlimLocalizations.delegates,
/// Configure slim localizations supported locales
supportedLocales: SlimLocalizations.supportedLocales..addAll([Locale('en', 'US')]),
),
);
}
}