π± Multilevel Navigator
ΠΠ΄Π°ΠΏΡΠΈΠ²Π½Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΎΠ½Π½Π°Ρ ΡΠΈΡΡΠ΅ΠΌΠ° Π΄Π»Ρ Flutter Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ 4 ΡΡΠΎΠ²Π½Π΅ΠΉ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΡΡ
ΠΌΠ½ΠΎΠ³ΠΎΡΡΠΎΠ²Π½Π΅Π²ΡΡ
ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠΎΠ² (list β detail β detail β detail) Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΏΠΎΠ΄ΡΡΡΠΎΠΉΠΊΠΎΠΉ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° Π²ΠΈΠ΄ΠΈΠΌΡΡ
ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ ΠΏΠΎΠ΄ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π°. ΠΠΎΡΡΡΠΎΠ΅Π½Π° ΠΏΠΎΠ²Π΅ΡΡ
go_router.
β¨ ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
- 4-ΡΡΠΎΠ²Π½Π΅Π²Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ (level1 β ΠΏΠ°Π½Π΅Π»Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ, level2 β ΡΠΏΠΈΡΠΎΠΊ, level3/level4 β Π΄Π΅ΡΠ°Π»ΠΈ) Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΌ ΡΠ°ΡΡΡΡΠΎΠΌ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ ΡΡΠΎΠ²Π½Π΅ΠΉ ΠΏΠΎΠ΄ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π°
- Resizable-ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ β ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΡΡΠ½Π΅Ρ ΡΠ°Π·Π΄Π΅Π»ΠΈΡΠ΅Π»Ρ ΠΈ ΠΌΠ΅Π½ΡΠ΅Ρ ΡΠΈΡΠΈΠ½Ρ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ (
SplitPane); ΠΌΠΎΠΆΠ½ΠΎ Π·Π°Π΄Π°ΡΡ ΠΈ Π½Π°ΡΠ°Π»ΡΠ½ΡΡ ΡΠΈΡΠΈΠ½Ρ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ per-ΡΠΈΡΠ° - Π‘Π²ΠΎΡΠ°ΡΠΈΠ²Π°Π½ΠΈΠ΅ level2/level3/level4 β ΠΏΠΎ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠΌΡ ΡΠ°ΠΏΡ Π½Π° Π°ΠΊΡΠΈΠ²Π½ΡΡ ΡΠΈΡΡ (
Level2CollapseMode) ΠΈΠ»ΠΈ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΡ ΠΏΡΠΈ ΡΡ ΠΎΠ΄Π΅ Ρ ΡΡΠΎΠ²Π½Ρ (MobileLevelCollapseMode), Ρ ΡΠΎΡ ΡΠ°Π½Π΅Π½ΠΈΠ΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ Π²ΠΈΠ΄ΠΆΠ΅ΡΠ°/BLoC - Π‘ΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ:
- Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π΅Ρ ΠΏΠΎΠ»Π½ΡΠΉ URI ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΡΠΈΡΠΈ ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅ΠΊΠ»ΡΡΠ΅Π½ΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠΈΡΠ°ΠΌΠΈ
- ΡΠΎΡ
ΡΠ°Π½ΡΠ΅Ρ Π²ΠΈΠ΄ΠΆΠ΅ΡΡ ΠΈ BLoC ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠΊΠ°Π·ΠΎΠΌ/ΡΠΊΡΡΡΠΈΠ΅ΠΌ ΡΡΠΎΠ²Π½Ρ (
PersistentBlocProvider)
- ΠΠΎΠ΄Π°Π»ΡΠ½ΡΠ΅ ΠΎΠΊΠ½Π° ΡΠ΅ΡΠ΅Π· query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ
?modal=id, Ρ ΡΠ°Π·Π΄Π΅Π»ΡΠ½ΡΠΌΠΈ Π±ΠΈΠ»Π΄Π΅ΡΠ°ΠΌΠΈ ΠΏΠΎΠ΄ ΠΏΠ»Π°ΡΡΠΎΡΠΌΡ (mobile/desktop/web) ΠΈ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π° (dialog/bottom sheet) - Π‘ΠΈΡΡΠ΅ΠΌΠ° ΡΠ°Π±ΠΎΠ² Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠ΅ΠΉ Π°ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠ°Π±Π° Ρ URL
- ΠΠ²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ β ΡΠ°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΠΈΡ Π½Π° ΡΡΠ΅Π±ΡΡΡΠΈΠ΅ ΠΈ Π½Π΅ ΡΡΠ΅Π±ΡΡΡΠΈΠ΅ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ, ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΡ ΡΠ΅ΡΠ΅Π·
AuthenticationStateNotifier - Dependency Injection β DI-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ ΠΏΠΎΠ²Π΅ΡΡ
InheritedWidget, ΡΠΊΡΡΡΡΠΉ Π·Π°MultilevelNavigatorProvider
ποΈ ΠΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ°
Π‘ΡΡΡΠΊΡΡΡΠ° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ:
lib/
βββ core/ # Π―Π΄ΡΠΎ
β βββ cache/ # LRU-ΠΊΡΡ (Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠΉ ΠΌΠ΅Ρ
Π°Π½ΠΈΠ·ΠΌ, Π½Π΅ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠΉ API β ΡΠΌ. Π½ΠΈΠΆΠ΅)
β βββ contracts/ # AuthenticationStateNotifier, RouterParams
β βββ models/ # Level, DisplayMode, VisibilityMap, FeatureMultilevelRules, Level2CollapseMode, MobileLevelCollapseMode
β βββ providers/ # PersistentBlocProvider
β βββ utils/ # MultilevelDimensions, NavigationUtils, NavigationStorage, PathUtils
β βββ validators/ # ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΡΠΈΡ
β
βββ features/ # Π€ΠΈΡΠΈ ΠΏΠ°ΠΊΠ΅ΡΠ° (Clean Architecture)
β βββ multilevel_layout/ # Π Π°ΡΡΡΡ layout: MultilevelBloc, LevelsWidthBloc, SplitPane
β βββ modal_system/ # ΠΠΎΠ΄Π°Π»ΡΠ½ΡΠ΅ ΠΎΠΊΠ½Π° ΡΠ΅ΡΠ΅Π· query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ `modal`
β βββ navigation/ # Π’Π°Π±Ρ, back button handler, mixins
β
βββ dependency_injection/ # MultilevelNavigatorDependencies (InheritedWidget)
βββ configuration/ # FeatureNavigationConfiguration, FeatureLevelItemConfiguration
βββ router/ # MultilevelNavigator, ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠ΅ GoRouter
βββ extensions/ # context.goNamed/pop/openModal/switchToFeature/...
βββ multilevel_navigator.dart # Public API (barrel-ΡΠ°ΠΉΠ»)
π ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ
1. Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
dependencies:
multilevel_navigator: ^1.0.0
2. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ
import 'package:multilevel_navigator/multilevel_navigator.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// ΠΠΎΡΡΠ΄ΠΎΠΊ Π²Π°ΠΆΠ΅Π½: ΡΠ½Π°ΡΠ°Π»Π° Π½Π΅Π°Π²ΡΠΎΡΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠΈΡΠΈ, ΠΏΠΎΡΠΎΠΌ Π°Π²ΡΠΎΡΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΠ΅
final features = [
AuthenticationNavigationConfiguration(),
ChatsNavigationConfiguration(),
CallsNavigationConfiguration(),
ProfileNavigationConfiguration(),
];
final multilevelNavigator = MultilevelNavigator(
features: features,
authenticationStateNotifier: AuthenticationStateNotifierImpl(authRepository),
// navigationPanelBuilder ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»Π΅Π½ β Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π½Π΅ ΡΠΈΡΡΠ΅Ρ level1 "ΠΈΠ· ΠΊΠΎΡΠΎΠ±ΠΊΠΈ",
// ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΠ°ΠΌΠΎ ΡΡΡΠΎΠΈΡ ΠΏΠ°Π½Π΅Π»Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ (sidebar/bottom nav) Π΄Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°Π½Π½ΠΎΠ³ΠΎ
// StatefulNavigationShell.
navigationPanelBuilder: (navigationShell) => NavigationPanel(navigationShell: navigationShell),
header: null, // ΠΎΠΏΡΠΈΠΎΠ½Π°Π»ΡΠ½ΡΠΉ header Π½Π°Π΄ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠΎΠΌ
overlay: null, // ΠΎΠΏΡΠΈΠΎΠ½Π°Π»ΡΠ½ΡΠΉ overlay ΠΏΠΎΠ²Π΅ΡΡ
Π²ΡΠ΅Π³ΠΎ layout
);
runApp(
MultilevelNavigatorProvider(
multilevelNavigator: multilevelNavigator,
child: MaterialApp.router(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true),
routerConfig: multilevelNavigator.router,
),
),
);
}
Π Π°Π±ΠΎΡΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠΎ Π²ΡΠ΅ΠΌΠΈ ΡΠΈΡΠ°ΠΌΠΈ (chats, calls, profile, authentication, guest) Π»Π΅ΠΆΠΈΡ Π² example/.
π ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΡΠΈΡΠΈ
ΠΠ°ΠΆΠ΄Π°Ρ ΡΠΈΡΠ° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ FeatureNavigationConfiguration:
final class ChatsNavigationConfiguration implements FeatureNavigationConfiguration {
const ChatsNavigationConfiguration();
@override
String? get initialLocation => null; // ΠΎΠΏΡΠΈΠΎΠ½Π°Π»ΡΠ½ΡΠΉ Π½Π°ΡΠ°Π»ΡΠ½ΡΠΉ ΠΏΡΡΡ ΡΠΈΡΠΈ
@override
bool get isVisibleInNavigation => true;
@override
bool get requiresAuthentication => true;
@override
String name(BuildContext context) => 'Π§Π°ΡΡ'; // ΠΌΠ΅ΡΠΎΠ΄, Π° Π½Π΅ Π³Π΅ΡΡΠ΅Ρ β ΠΌΠΎΠΆΠ½ΠΎ Π»ΠΎΠΊΠ°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ ΡΠ΅ΡΠ΅Π· context
@override
String get key => 'chats'; // ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΊΠ»ΡΡ ΡΠΈΡΠΈ (ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π² switchToFeature ΠΈ Ρ.ΠΏ.)
@override
IconData get emoji => Icons.chat;
@override
String get basePath => 'chats';
@override
FeatureLevelItemConfiguration get level2 => FeatureLevelItemConfiguration(
path: '',
name: 'chats_level2',
builder: (context) => const ChatsLevel2Page(),
placeholderBuilder: (context) => const ChatLevel2Placeholder(),
);
@override
List<FeatureLevelItemConfiguration> get level3 => [
FeatureLevelItemConfiguration(
path: ':chatId',
name: 'chats_level3_messages',
builder: (context) => ChatsLevel3MessagesPage(chatId: _chatId(context)),
placeholderBuilder: (context) => const ChatLevel3Placeholder(),
parentName: 'chats_level2',
),
];
@override
List<FeatureLevelItemConfiguration> get level4 => const [];
@override
List<MultilevelModalBuilderAbstraction> get modals => [ModalCall(), ModalVideo()];
// ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅ΡΡ, Π΄ΠΎΡΡΡΠΏΠ½ΡΠ΅ ΠΈΠ· BuildContext Π²ΠΎ Π²ΡΠ΅Ρ
ΡΡΠΎΠ²Π½ΡΡ
(level2-4) ΡΠΈΡΠΈ.
// ΠΡΠΈ implements (Π² ΠΎΡΠ»ΠΈΡΠΈΠ΅ ΠΎΡ extends) ΡΡΠΎΡ Π³Π΅ΡΡΠ΅Ρ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»Π΅Π½.
@override
List<SingleChildWidget> get providers => const [];
}
ΠΡΡΠ°Π»ΡΠ½ΡΠ΅ ΡΠ»Π΅Π½Ρ ΠΊΠΎΠ½ΡΡΠ°ΠΊΡΠ° ΠΈΠΌΠ΅ΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ (ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠΉΡΠ΅ ΠΏΠΎ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ):
| Π§Π»Π΅Π½ | ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ |
|---|---|
defaultLevel2Path |
ΠΏΡΡΡ level2 ΡΠΈΡΠΈ, ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ /$basePath |
rules |
FeatureMultilevelRules β ΠΊΠ°ΠΊΠΈΠ΅ ΡΡΠΎΠ²Π½ΠΈ Π²ΠΈΠ΄Π½Ρ ΠΏΡΠΈ ΠΊΠ°ΠΊΠΎΠΉ Π³Π»ΡΠ±ΠΈΠ½Π΅ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΈΠ· ΡΠ΅ΠΆΠΈΠΌΠΎΠ² ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ FeatureMultilevelDefaultRules) |
collapseMode |
Level2CollapseMode.remove (ΡΠ±ΡΠ°ΡΡ level2 ΠΈΠ· Π΄Π΅ΡΠ΅Π²Π°) ΠΈΠ»ΠΈ .hide (ΡΠΊΡΡΡΡ, ΡΠΈΡΠΈΠ½Π° 0, ΡΠΎΡ
ΡΠ°Π½ΠΈΠ² state level3/level4) ΠΏΡΠΈ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠΌ ΡΠ°ΠΏΠ΅ Π½Π° Π°ΠΊΡΠΈΠ²Π½ΡΡ ΡΠΈΡΡ |
collapseAnimationDuration |
Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ ΡΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π½ΠΈΡ level2 (ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ .hide) |
level3CollapseMode / level4CollapseMode |
MobileLevelCollapseMode.preserve (ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ, ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠΎΡ
ΡΠ°Π½ΡΠ΅ΡΡΡ) ΠΈΠ»ΠΈ .discard (ΠΎΠ±ΡΡΠ½Π°Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ Π½Π°Π·Π°Π΄) ΠΏΡΠΈ ΡΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π½ΠΈΠΈ level3/level4 Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΡ
|
initialLevel2Width / initialLevel3Width / initialLevel4Width |
Π½Π°ΡΠ°Π»ΡΠ½Π°Ρ ΡΠΈΡΠΈΠ½Π° ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ Π² ΠΏΠΈΠΊΡΠ΅Π»ΡΡ ΠΏΡΠΈ ΠΏΠ΅ΡΠ²ΠΎΠΌ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠΈ layout ΡΠΈΡΠΈ (Π΄ΠΎ ΡΡΡΠ½ΠΎΠ³ΠΎ resize); Π΄Π»Ρ level3 Π΄Π΅ΠΉΡΡΠ²ΡΠ΅Ρ ΠΊΠ°ΠΊ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ ΡΠΈΡΠΈΠ½Π° |
FeatureLevelItemConfiguration (level2/level3/level4-ΡΠ»Π΅ΠΌΠ΅Π½Ρ): path, name (ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎΠ΅ ΠΈΠΌΡ ΡΠΎΡΡΠ°), builder, placeholderBuilder (ΡΠ΅Π½Π΄Π΅ΡΠΈΡΡΡ, ΠΊΠΎΠ³Π΄Π° ΡΡΠΎΠ²Π΅Π½Ρ ΡΠΊΡΡΡ ΠΏΠΎ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ), parentName (ΡΠ²ΡΠ·ΡΠ²Π°Π΅Ρ ΡΡΠΎΠ²Π΅Π½Ρ Ρ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΌ), providers (Π΄ΠΎΡΡΡΠΏΠ½Ρ ΡΠΎΠ»ΡΠΊΠΎ Π² ΠΏΠΎΠ΄Π΄Π΅ΡΠ΅Π²Π΅ ΡΡΠΎΠ³ΠΎ ΡΡΠΎΠ²Π½Ρ).
π§© ΠΠ΄Π°ΠΏΡΠΈΠ²Π½ΠΎΡΡΡ: ΡΡΠΎΠ²Π½ΠΈ, Π±ΡΠ΅ΠΉΠΊΠΏΠΎΠΈΠ½ΡΡ, ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ
ΠΠΈΠ΄ΠΈΠΌΠΎΡΡΡ ΡΡΠΎΠ²Π½Π΅ΠΉ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΡΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎΠΌ ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΡΠΌΠ΅ΡΠ°Π΅ΡΡΡ Π² ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π° (MultilevelDimensions, lib/core/utils/multilevel_dimensions.dart):
level1Width = 50 // ΠΏΠ°Π½Π΅Π»Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ
level2Width = 420
level3MinWidth = 750 // level3 β Π³ΠΈΠ±ΠΊΠ°Ρ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠ°, ΡΡΠ½Π΅Ρ ΠΎΡΡΠ°Π²ΡΠ΅Π΅ΡΡ ΠΌΠ΅ΡΡΠΎ, ΡΡΠΎ ΠΌΠΈΠ½ΠΈΠΌΡΠΌ
level4Width = 250
widthForTwoColumns = 800
widthForThreeColumns = level1Width + level2Width + level3MinWidth // = 1220
widthForFourColumns = widthForThreeColumns + level4Width // = 1470
ΠΠΎΡΠΎΠ³ΠΈ ΡΡΡΡ
- ΠΈ ΡΠ΅ΡΡΡΡΡ
ΠΊΠΎΠ»ΠΎΠ½ΠΎΡΠ½ΠΎΠ³ΠΎ ΡΠ΅ΠΆΠΈΠΌΠ° Π²ΡΡΠΈΡΠ»ΡΡΡΡΡ ΠΈΠ· ΡΠΈΡΠΈΠ½ ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ, Π° Π½Π΅ Π·Π°Π΄Π°Π½Ρ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌΠΈ ΠΊΠΎΠ½ΡΡΠ°Π½ΡΠ°ΠΌΠΈ β ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ level2Width ΡΠ΄Π²ΠΈΠ³Π°Π΅Ρ ΠΈ ΠΏΠΎΡΠΎΠ³ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π° Π½Π° 3 ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ.
Π€Π°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ΅ΠΆΠΈΠΌ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ β DisplayMode (5 Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ, Π° Π½Π΅ ΠΏΡΠΎΡΡΠΎ Β«N ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊΒ»):
floatingPanelβ ΡΠ·ΠΊΠΈΠΉ ΡΠΊΡΠ°Π½, level1 ΠΏΠΎΠΊΠ°Π·Π°Π½ ΠΊΠ°ΠΊ ΠΏΠ»Π°Π²Π°ΡΡΠ°Ρ ΠΏΠ°Π½Π΅Π»ΡsingleColumnβ ΠΎΠ΄Π½Π° ΠΊΠΎΠ»ΠΎΠ½ΠΊΠ° Π½Π° Π²Π΅ΡΡ ΡΠΊΡΠ°Π½singleColumnWithBottomNavβ ΠΎΠ΄Π½Π° ΠΊΠΎΠ»ΠΎΠ½ΠΊΠ° + Π½ΠΈΠΆΠ½ΡΡ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡfixedMultiColumnβ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ ΡΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΡΠΈΡΠΈΠ½ΡresizableMultiColumnβ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ Ρ ΡΡΡΠ½ΡΠΌ resize (SplitPane)
ΠΠ°ΠΊΠΈΠ΅ ΠΈΠΌΠ΅Π½Π½ΠΎ ΡΡΠΎΠ²Π½ΠΈ (ΠΈ ΠΏΠ»Π΅ΠΉΡΡ
ΠΎΠ»Π΄Π΅ΡΡ) Π²ΠΈΠ΄Π½Ρ ΠΏΡΠΈ Π΄Π°Π½Π½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅ ΠΈ ΡΠ΅ΠΊΡΡΠ΅ΠΉ Π³Π»ΡΠ±ΠΈΠ½Π΅ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ (1β4), ΡΠ΅ΡΠ°Π΅Ρ FeatureMultilevelRules.{single,two,three,four}ColumnVisibility(activeDepth) β ΡΠΈΡΠ° ΠΌΠΎΠΆΠ΅Ρ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ rules, ΡΡΠΎΠ±Ρ Π·Π°Π΄Π°ΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΡ ΠΌΠ°ΡΡΠΈΡΡ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ Π²ΠΌΠ΅ΡΡΠΎ FeatureMultilevelDefaultRules.
πΎ Π‘ΠΎΡ ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ
- URI ΡΠΈΡΠΈ Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π΅ΡΡΡ ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅ΠΊΠ»ΡΡΠ΅Π½ΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠΈΡΠ°ΠΌΠΈ ΠΈ Π²ΠΎΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΡΡΡ ΠΏΡΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ΅ (
NavigationStorage, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π²context.switchToFeature). - BLoC ΡΡΠΎΠ²Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ ΡΠΊΡΡΠ²Π°ΡΡΡΡ ΠΏΠΎ Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΡΠΌ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ, ΡΠΎΡ
ΡΠ°Π½ΡΠ΅ΡΡΡ ΡΠ΅ΡΠ΅Π·
PersistentBlocProviderΠ²ΠΌΠ΅ΡΡΠΎ ΠΎΠ±ΡΡΠ½ΠΎΠ³ΠΎBlocProvider:
FeatureLevelItemConfiguration(
path: ':chatId',
name: 'chats_level3_messages',
builder: (context) => const ChatsLevel3MessagesPage(),
placeholderBuilder: (context) => const ChatLevel3Placeholder(),
providers: [
PersistentBlocProvider<ChatMessagesBloc>(
cacheKey: 'chat_messages_${GoRouterState.of(context).pathParameters['chatId']}',
create: (context) => ChatMessagesBloc()..add(LoadMessages()),
),
],
)
ΠΠ½ΡΡΠ°Π½Ρ BLoC ΠΆΠΈΠ²ΡΡ Π² ΠΊΡΡΠ΅ Π½Π° ΡΡΠΎΠ²Π½Π΅ ΡΠΈΡΠΈ (FeatureBlocCacheScope) ΠΈ ΠΏΠ΅ΡΠ΅ΠΆΠΈΠ²Π°Π΅Ρ ΡΠΊΡΡΡΠΈΠ΅/ΠΏΠΎΠΊΠ°Π· ΡΡΠΎΠ²Π½Ρ β ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π½Π΅ ΡΠ±ΡΠ°ΡΡΠ²Π°Π΅ΡΡΡ, ΠΏΠΎΠΊΠ° ΡΠ°ΠΌΠ° ΡΠΈΡΠ° ΡΠΌΠΎΠ½ΡΠΈΡΠΎΠ²Π°Π½Π°.
π ΠΠ²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ
class AuthenticationStateNotifierImpl extends AuthenticationStateNotifier {
AuthenticationStateNotifierImpl(this._authRepository) {
_subscription = _authRepository.authStateStream.listen((_) => notifyListeners());
}
final AuthRepository _authRepository;
StreamSubscription? _subscription;
@override
Future<bool> get isAuthenticated => _authRepository.isAuthenticated();
@override
Future<bool> get isGuest async => false; // ΠΎΠΏΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎ: Π³ΠΎΡΡΠ΅Π²ΠΎΠΉ ΡΠ΅ΠΆΠΈΠΌ
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
}
AuthenticationStateNotifier β ΡΡΠΎ ChangeNotifier Ρ Π΄Π²ΡΠΌΡ Π³Π΅ΡΡΠ΅ΡΠ°ΠΌΠΈ (isAuthenticated, isGuest), Π° Π½Π΅ Ρ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌΠΈ. Π€ΠΈΡΠΈ Ρ requiresAuthentication: false (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠΊΡΠ°Π½ Π»ΠΎΠ³ΠΈΠ½Π° ΠΈΠ»ΠΈ Π³ΠΎΡΡΠ΅Π²ΠΎΠΉ ΡΠ΅ΠΆΠΈΠΌ) Π΄ΠΎΡΡΡΠΏΠ½Ρ Π±Π΅Π· Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ; ΠΏΡΠΈ true β ΡΠΎΡΡΠ΅Ρ ΡΠ΅Π΄ΠΈΡΠ΅ΠΊΡΠΈΡ Π½Π΅Π°Π²ΡΠΎΡΠΈΠ·ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΠΎΠΆΠ½ΠΎ Π·Π°Π²Π΅ΡΡΠΈ Π»ΡΠ±ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π½Π΅Π°Π²ΡΠΎΡΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΡ
ΡΠΈΡ.
πͺ ΠΠΎΠ΄Π°Π»ΡΠ½ΡΠ΅ ΠΎΠΊΠ½Π°
ΠΠΎΠ΄Π°Π»ΠΊΠΈ ΠΎΡΠΊΡΡΠ²Π°ΡΡΡΡ ΡΠ΅ΡΠ΅Π· query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ ?modal=id ΠΈ ΡΠ΅Π½Π΄Π΅ΡΡΡΡΡ ΠΊΠ°ΠΊ dialog ΠΈΠ»ΠΈ bottom sheet β ΡΠ°Π·Π΄Π΅Π»ΡΠ½ΠΎ Π΄Π»Ρ ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΡ
/Π΄Π΅ΡΠΊΡΠΎΠΏΠ°/Π²Π΅Π±Π° ΠΈ Π΄Π»Ρ ΠΎΠ΄Π½ΠΎ-/ΠΌΠ½ΠΎΠ³ΠΎΠΊΠΎΠ»ΠΎΠ½ΠΎΡΠ½ΠΎΠ³ΠΎ ΡΠ΅ΠΆΠΈΠΌΠ°. Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΡΠΉ API β MultilevelModalPlatformBuilder:
class ModalCall implements MultilevelModalPlatformBuilder {
@override
String get id => 'call';
@override
bool get isDismissible => true;
@override
PlatformModalBuilders? get mobile => PlatformModalBuilders(
singleColumn: PlatformWidthModalBuilders(
bottomSheetBuilder: (context) => const CallBottomSheet(),
),
multiColumn: PlatformWidthModalBuilders(
dialogBuilder: (context) => const CallDialog(),
),
);
@override
PlatformModalBuilders get desktop => PlatformModalBuilders(
singleColumn: PlatformWidthModalBuilders(dialogBuilder: (context) => const CallDialog()),
multiColumn: PlatformWidthModalBuilders(dialogBuilder: (context) => const CallDialog()),
);
@override
PlatformModalBuilders? get web => desktop;
}
Π Π΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ Π² modals ΡΠΈΡΠΈ (ΡΠΌ. ΠΏΡΠΈΠΌΠ΅Ρ Π²ΡΡΠ΅) ΠΈ ΠΎΡΠΊΡΡΠ²Π°Π΅ΡΡΡ/Π·Π°ΠΊΡΡΠ²Π°Π΅ΡΡΡ ΡΠ΅ΡΠ΅Π· ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ:
context.openModal('call');
context.closeModal('call');
// Ρ ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ°
final result = await context.openModalAsync<String>('call');
context.closeModalWithResult('call', 'done');
// Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ»ΡΠ½ΡΠΌΠΈ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ°ΠΌΠΈ Π²ΠΌΠ΅ΡΡΠΎ query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
context.openModalWithArgs('call', callId);
final callId = context.readModalArgs<String>('call'); // Π²Π½ΡΡΡΠΈ Π±ΠΈΠ»Π΄Π΅ΡΠ° ΠΌΠΎΠ΄Π°Π»ΠΊΠΈ
Π‘ΡΠ°ΡΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ MultilevelModalBuilder (Π΅Π΄ΠΈΠ½ΡΠΉ dialogBuilder/bottomSheetBuilder Π±Π΅Π· ΡΠ°Π·Π΄Π΅Π»Π΅Π½ΠΈΡ ΠΏΠΎ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ΅) ΠΏΠΎΠΌΠ΅ΡΠ΅Π½ @Deprecated β ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ MultilevelModalPlatformBuilder Π² Π½ΠΎΠ²ΠΎΠΌ ΠΊΠΎΠ΄Π΅.
ποΈ Π’Π°Π±Ρ
TabManagerConfiguration Π°Π²ΡΠΎΠ³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎΠ΅ ΠΈΠΌΡ query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ° ΠΈΠ· ΡΠΈΠΏΠ° ΡΡΡΠ°Π½ΠΈΡΡ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ChatsLevel2Page β chats_level2_page_tab), ΡΠ°ΠΊ ΡΡΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΡΡ
Π½Π°Π±ΠΎΡΠΎΠ² ΡΠ°Π±ΠΎΠ² Π½Π° ΡΠ°Π·Π½ΡΡ
ΡΡΠΎΠ²Π½ΡΡ
Π½Π΅ ΠΊΠΎΠ½ΡΠ»ΠΈΠΊΡΡΡΡ:
class ChatsLevel2Page extends StatefulWidget {
const ChatsLevel2Page({super.key});
@override
State<ChatsLevel2Page> createState() => _ChatsLevel2PageState();
}
class _ChatsLevel2PageState extends State<ChatsLevel2Page>
with TickerProviderStateMixin, TabControllerMixin {
@override
void didChangeDependencies() {
super.didChangeDependencies();
initializeTabController(
TabManagerConfiguration(
ChatsLevel2Page,
tabs: const [
TabConfiguration(name: 'all', title: 'ΠΡΠ΅', icon: Icons.chat, content: AllChatsTab()),
TabConfiguration(name: 'unread', title: 'ΠΠ΅ΠΏΡΠΎΡΠΈΡΠ°Π½Π½ΡΠ΅', icon: Icons.mark_chat_unread, content: UnreadChatsTab()),
],
),
);
syncTabControllerWithQuery();
}
@override
Widget build(BuildContext context) {
syncTabControllerWithQuery();
return MultilevelTabContent(
tabController: tabController,
configuration: configuration!,
onTabChanged: switchToTab,
);
}
}
π§ ΠΠ°Π²ΠΈΠ³Π°ΡΠΈΡ ΠΈΠ· ΠΊΠΎΠ΄Π°
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ Π΄ΠΎΡΡΡΠΏΠ½Ρ ΠΊΠ°ΠΊ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Π½Π° BuildContext (ΡΠΎΡ
ΡΠ°Π½ΡΡΡ ΡΠ΅ΠΊΡΡΠΈΠ΅ query-ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ/extra/ΡΡΠ°Π³ΠΌΠ΅Π½Ρ ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Π΅):
context.goNamed('chats_level3_messages', pathParameters: {'chatId': id});
context.pop(level: 3); // Π·Π°ΠΊΡΡΡΡ level3 ΠΈ Π²ΡΡ, ΡΡΠΎ Π²ΡΡΠ΅
context.switchToFeature(ChatsNavigationConfiguration()); // Ρ Π²ΠΎΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ΠΌ ΡΠΎΡ
ΡΠ°Π½ΡΠ½Π½ΠΎΠ³ΠΎ URI ΡΠΈΡΠΈ
context.toggleLevel2(); // ΡΠ²Π΅ΡΠ½ΡΡΡ/ΡΠ°Π·Π²Π΅ΡΠ½ΡΡΡ level2 (Level2CollapseMode.hide)
context.collapseMobileLevel3(); // ΡΠ²Π΅ΡΠ½ΡΡΡ level3 Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΠΎΠΌ, ΡΠΎΡ
ΡΠ°Π½ΠΈΠ² state (ΠΈΠ»ΠΈ .discard β ΡΠΌ. level3CollapseMode)
context.expandMobileLevel3();
βοΈ ΠΠ½ΡΡΡΠ΅Π½Π½Π΅Π΅ ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎ (Π½Π΅ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΠΉ API)
- DI β
MultilevelNavigatorDependencies(InheritedWidget) ΡΠΎΠ±ΠΈΡΠ°Π΅ΡMultilevelBloc, back-button handler ΠΈ Π΄ΡΡΠ³ΠΈΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ; ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ ΡΠΊΡΡΡ Π·Π°MultilevelNavigatorProvider, ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΎΠ±ΡΠ°ΡΠ°ΡΡΡΡ ΠΊ Π½Π΅ΠΌΡ Π½Π°ΠΏΡΡΠΌΡΡ Π½Π΅ Π½ΡΠΆΠ½ΠΎ. - ΠΡΡ-ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ (
core/cache/*, LRU Ρ TTL/ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΎΠΉ) ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π²Π½ΡΡΡΠΈMultilevelBlocΠΈSplitPaneCalculatorΠ΄Π»Ρ ΠΊΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠΉ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ/layout/ΡΠΎΡ ΡΠ°Π½ΡΠ½Π½ΡΡ ΡΠΈΡΠΈΠ½ ΠΊΠΎΠ»ΠΎΠ½ΠΎΠΊ. ΠΡΠΎ Π²Π½ΡΡΡΠ΅Π½Π½ΡΡ ΠΈΠ½ΡΡΠ°ΡΡΡΡΠΊΡΡΡΠ° ΠΏΠ°ΠΊΠ΅ΡΠ° β ΠΊΠ»Π°ΡΡΡCacheManagerImpl/CacheKeys/CacheNamesΠ½Π΅ ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΡΡΡΡ ΠΈΠ·package:multilevel_navigator/multilevel_navigator.dartΠΈ Π½Π΅ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Ρ Π΄Π»Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Π² ΠΊΠΎΠ΄Π΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ.
π§ͺ Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
Π’Π΅ΡΡΠ°ΠΌΠΈ ΠΏΠΎΠΊΡΡΡΠΎ ΡΠ΄ΡΠΎ Π°Π΄Π°ΠΏΡΠΈΠ²Π½ΠΎΡΡΠΈ:
test/
βββ core/cache/cache_manager_test.dart
βββ core/utils/navigation_utils_test.dart
βββ core/utils/path_utils_test.dart
βββ features/multilevel_layout/application/bloc/multilevel_bloc_test.dart
βββ features/multilevel_layout/presentation/widgets/split/split_pane_test.dart
βββ router/feature_configuration_resolver_test.dart
ΠΠΎΠ΄Π°Π»ΡΠ½Π°Ρ ΡΠΈΡΡΠ΅ΠΌΠ°, ΡΠ°Π±Ρ, DI-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ ΠΈ PersistentBlocProvider ΡΠ΅ΡΡΠ°ΠΌΠΈ ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΠΎΠΊΡΡΡΡ.
π ΠΡΡΠΎΡΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ
ΠΠΊΡΡΠ°Π»ΡΠ½ΡΠΉ ΡΠΏΠΈΡΠΎΠΊ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΏΠΎ Π²Π΅ΡΡΠΈΡΠΌ β Π² CHANGELOG.md.
Libraries
- configuration/feature_level_configuration
- core/cache/cache_container
- core/cache/cache_key
- core/cache/cache_keys
- core/cache/cache_manager
- core/cache/cache_manager_contract
- core/cache/cache_name
- core/cache/cache_names
- core/cache/cache_stats
- core/constants/display_mode_constants
- core/constants/level_constants
- core/contracts/authentication_state_notifier
- core/contracts/router_params
- core/factories/router_factory
- core/models/display_mode
- core/models/feature_multilevel_default_rules
- core/models/feature_multilevel_rules
- core/models/level
- core/models/level2_collapse_mode
- core/models/mobile_level_collapse_mode
- core/models/multilevel_layout_rule
- core/models/visibility_map
- core/providers/persistent_bloc_provider
- core/utils/multilevel_dimensions
- core/utils/path_utils
- core/utils/validation_utils
- core/validators/feature_configuration_validator
- features/modal_system/application/managers/multilevel_modal_manager
- features/modal_system/domain/models/modal_bottom_sheet_options
- features/modal_system/domain/models/modal_dialog_options
- features/modal_system/domain/models/modal_platform
- features/modal_system/domain/models/modal_screen_width
- features/modal_system/domain/models/multilevel_modal_builder
- features/modal_system/domain/models/multilevel_modal_builder_abstraction
- features/modal_system/domain/models/platform_modal_builders
- features/modal_system/domain/models/platform_width_modal_builders
- features/modal_system/infrastructure/utils/modal_builder_resolver
- features/modal_system/infrastructure/utils/modal_platform_resolver
- features/modal_system/presentation/mixins/auto_close_popup_mixin
- features/modal_system/presentation/widgets/multilevel_bottom_sheet_wrapper
- features/modal_system/presentation/widgets/multilevel_dialog_wrapper
- features/modal_system/presentation/widgets/multilevel_modal_overlay
- features/multilevel_layout/application/bloc/levels_width_bloc
- features/multilevel_layout/application/bloc/multilevel_bloc
- features/multilevel_layout/domain/models/levels_width
- features/multilevel_layout/domain/services/layout_calculator_service
- features/multilevel_layout/domain/services/visibility_resolver_service
- features/multilevel_layout/infrastructure/observers/route_change_observer
- features/multilevel_layout/infrastructure/observers/screen_metrics_observer
- features/multilevel_layout/infrastructure/services/layout_calculator_service_impl
- features/multilevel_layout/infrastructure/services/visibility_resolver_service_impl
- features/multilevel_layout/presentation/layouts/multi_column_layout
- features/multilevel_layout/presentation/layouts/multilevel_layout
- features/multilevel_layout/presentation/layouts/simple_row_layout
- features/multilevel_layout/presentation/layouts/single_column_layout
- features/multilevel_layout/presentation/layouts/split_pane_layout
- features/multilevel_layout/presentation/utils/split_pane_calculator
- features/multilevel_layout/presentation/widgets/default/default_not_found
- features/multilevel_layout/presentation/widgets/default/default_placeholder
- features/multilevel_layout/presentation/widgets/split/split_pane
- features/multilevel_layout/presentation/widgets/split/utils
- features/multilevel_layout/presentation/widgets/split_pane_widget
- features/multilevel_layout/presentation/widgets/splitter_widget
- features/navigation/domain/models/tab_configuration
- features/navigation/domain/models/tab_manager_configuration
- features/navigation/presentation/mixins/tab_controller_mixin
- features/navigation/presentation/widgets/feature_bloc_cache
- features/navigation/presentation/widgets/feature_scope
- features/navigation/presentation/widgets/feature_state_scope
- features/navigation/presentation/widgets/multilevel_tab_bar
- features/navigation/presentation/widgets/multilevel_tab_content
- features/navigation/presentation/widgets/multilevel_tab_page
- router/current_feature_configuration
- router/feature_configuration_resolver
- router/page_builder
- router/route/auth_route
- router/route/unauth_route
- router/route_builder