Skip to content

Commit

Permalink
NavigationDrawer control (#2059)
Browse files Browse the repository at this point in the history
* Create navigation_drawer.py

* added end_drawer to view and page

* navigation_drawer.dart

* NavigationDrawer prototype

* NavigationDrawerDestination icon property

* icon_content for destination

* destinations changed to controls

* removed bgColor property for NavigationDrawerDestination as it doesn't work in flutter

flutter/flutter#138105

* Update navigation_drawer.dart

* selected_icon and selected_icon_content

* elevation property

* indicator_color property

* indicator_shape property

* shadow_color property

* surface_tint_color property

* refactor

* added elevation and shadow_color to NavigationBar

* indicator_color, indicator_shape, surface_tint_color, shadow_color, elevation for NavigationBar

* show_drawer, show_end_drawer, close_drawer, close_end_drawer for page

* on_dismiss for NavigationDrawer

* updated comments

---------

Co-authored-by: Feodor Fitsner <[email protected]>
  • Loading branch information
InesaFitsner and FeodorFitsner authored Nov 10, 2023
1 parent 2a2d063 commit 5cde674
Show file tree
Hide file tree
Showing 8 changed files with 771 additions and 17 deletions.
9 changes: 9 additions & 0 deletions package/lib/src/controls/navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../protocol/update_control_props_payload.dart';
import '../utils/colors.dart';
import '../utils/icons.dart';
import 'create_control.dart';
import '../utils/borders.dart';

class NavigationBarControl extends StatefulWidget {
final Control? parent;
Expand Down Expand Up @@ -79,6 +80,14 @@ class _NavigationBarControlState extends State<NavigationBarControl> {
labelBehavior: labelBehavior,
height: widget.control.attrDouble("height"),
elevation: widget.control.attrDouble("elevation"),
shadowColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("shadowColor", "")!),
surfaceTintColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("surfaceTintColor", "")!),
indicatorColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("indicatorColor", "")!),
indicatorShape:
parseOutlinedBorder(widget.control, "indicatorShape"),
backgroundColor: HexColor.fromString(
Theme.of(context), widget.control.attrString("bgColor", "")!),
selectedIndex: _selectedIndex,
Expand Down
128 changes: 128 additions & 0 deletions package/lib/src/controls/navigation_drawer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';

import '../actions.dart';
import '../flet_app_services.dart';
import '../models/app_state.dart';
import '../models/control.dart';
import '../models/controls_view_model.dart';
import '../protocol/update_control_props_payload.dart';
import '../utils/colors.dart';
import '../utils/icons.dart';
import 'create_control.dart';
import '../utils/borders.dart';
import '../utils/edge_insets.dart';

class NavigationDrawerControl extends StatefulWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;
final dynamic dispatch;

const NavigationDrawerControl(
{Key? key,
this.parent,
required this.control,
required this.children,
required this.parentDisabled,
required this.dispatch})
: super(key: key);

@override
State<NavigationDrawerControl> createState() =>
_NavigationDrawerControlState();
}

class _NavigationDrawerControlState extends State<NavigationDrawerControl> {
int _selectedIndex = 0;

void _destinationChanged(int index) {
_selectedIndex = index;
debugPrint("Selected index: $_selectedIndex");
List<Map<String, String>> props = [
{"i": widget.control.id, "selectedindex": _selectedIndex.toString()}
];
widget.dispatch(
UpdateControlPropsAction(UpdateControlPropsPayload(props: props)));
final server = FletAppServices.of(context).server;
server.updateControlProps(props: props);
server.sendPageEvent(
eventTarget: widget.control.id,
eventName: "change",
eventData: _selectedIndex.toString());
}

@override
Widget build(BuildContext context) {
debugPrint("NavigationDrawerControl build: ${widget.control.id}");

bool disabled = widget.control.isDisabled || widget.parentDisabled;
var selectedIndex = widget.control.attrInt("selectedIndex", 0)!;

if (_selectedIndex != selectedIndex) {
_selectedIndex = selectedIndex;
}

var navDrawer = StoreConnector<AppState, ControlsViewModel>(
distinct: true,
converter: (store) => ControlsViewModel.fromStore(
store,
widget.children
.where((c) => c.isVisible && c.name == null)
.map((c) => c.id)),
builder: (content, viewModel) {
List<Widget> children = viewModel.controlViews.map((destView) {
if (destView.control.type == "navigationdrawerdestination") {
var icon =
getMaterialIcon(destView.control.attrString("icon", "")!);
var iconContentCtrls =
destView.children.where((c) => c.name == "icon_content");
var selectedIcon = getMaterialIcon(
destView.control.attrString("selectedIcon", "")!);
var selectedIconContentCtrls = destView.children
.where((c) => c.name == "selected_icon_content");
return NavigationDrawerDestination(
// backgroundColor: HexColor.fromString(Theme.of(context),
// destView.control.attrString("bgColor", "")!),
// flutter issue https://github.com/flutter/flutter/issues/138105
icon: iconContentCtrls.isNotEmpty
? createControl(
destView.control, iconContentCtrls.first.id, disabled)
: Icon(icon),
label: Text(destView.control.attrString("label", "")!),
selectedIcon: selectedIconContentCtrls.isNotEmpty
? createControl(destView.control,
selectedIconContentCtrls.first.id, disabled)
: selectedIcon != null
? Icon(selectedIcon)
: null,
);
} else {
return createControl(
widget.control, destView.control.id, disabled);
}
}).toList();
return NavigationDrawer(
elevation: widget.control.attrDouble("elevation"),
indicatorColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("indicatorColor", "")!),
indicatorShape:
parseOutlinedBorder(widget.control, "indicatorShape"),
backgroundColor: HexColor.fromString(
Theme.of(context), widget.control.attrString("bgColor", "")!),
selectedIndex: _selectedIndex,
shadowColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("shadowColor", "")!),
surfaceTintColor: HexColor.fromString(Theme.of(context),
widget.control.attrString("surfaceTintColor", "")!),
tilePadding: parseEdgeInsets(widget.control, "tilePadding") ??
const EdgeInsets.symmetric(horizontal: 12.0),
onDestinationSelected: _destinationChanged,
children: children,
);
});

return navDrawer;
}
}
127 changes: 119 additions & 8 deletions package/lib/src/controls/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '../models/page_args_model.dart';
import '../models/page_media_view_model.dart';
import '../models/routes_view_model.dart';
import '../protocol/keyboard_event.dart';
import '../protocol/update_control_props_payload.dart';
import '../routing/route_parser.dart';
import '../routing/route_state.dart';
import '../routing/router_delegate.dart';
Expand All @@ -34,6 +35,7 @@ import '../widgets/page_media.dart';
import '../widgets/window_media.dart';
import 'app_bar.dart';
import 'create_control.dart';
import 'navigation_drawer.dart';
import 'scroll_notification_control.dart';
import 'scrollable_control.dart';

Expand Down Expand Up @@ -90,6 +92,8 @@ class _PageControlState extends State<PageControl> {
late final RouteParser _routeParser;
String? _prevViewRoutes;
bool _keyboardHandlerSubscribed = false;
bool? _drawerOpened;
bool? _endDrawerOpened;

@override
void initState() {
Expand Down Expand Up @@ -598,6 +602,8 @@ class _PageControlState extends State<PageControl> {
Control? appBar;
Control? fab;
Control? navBar;
Control? drawer;
Control? endDrawer;
List<Widget> controls = [];
bool firstControl = true;

Expand All @@ -611,6 +617,13 @@ class _PageControlState extends State<PageControl> {
} else if (ctrl.type == "navigationbar") {
navBar = ctrl;
continue;
} else if (ctrl.type == "navigationdrawer" &&
ctrl.name == "start") {
drawer = ctrl;
continue;
} else if (ctrl.type == "navigationdrawer" && ctrl.name == "end") {
endDrawer = ctrl;
continue;
}
// spacer between displayed controls
else if (spacing > 0 &&
Expand All @@ -626,10 +639,8 @@ class _PageControlState extends State<PageControl> {
controls.add(createControl(control, ctrl.id, control.isDisabled));
}

List<String> childIds = [];
if (appBar != null) {
childIds.add(appBar.id);
}
List<String> childIds =
[appBar?.id, drawer?.id, endDrawer?.id].whereNotNull().toList();

final textDirection = parent.attrBool("rtl", false)!
? TextDirection.rtl
Expand All @@ -651,10 +662,12 @@ class _PageControlState extends State<PageControl> {
builder: (context, childrenViews) {
debugPrint("Route view StoreConnector build: $viewId");

var appBarView =
appBar != null && childrenViews.controlViews.isNotEmpty
? childrenViews.controlViews.last
: null;
var appBarView = childrenViews.controlViews.firstWhereOrNull(
(v) => v.control.id == (appBar?.id ?? ""));
var drawerView = childrenViews.controlViews.firstWhereOrNull(
(v) => v.control.id == (drawer?.id ?? ""));
var endDrawerView = childrenViews.controlViews.firstWhereOrNull(
(v) => v.control.id == (endDrawer?.id ?? ""));

var column = Column(
mainAxisAlignment: mainAlignment,
Expand All @@ -673,7 +686,55 @@ class _PageControlState extends State<PageControl> {
ScrollNotificationControl(control: control, child: child);
}

GlobalKey<ScaffoldState>? scaffoldKey =
FletAppServices.of(context).globalKeys["_scaffold"]
as GlobalKey<ScaffoldState>?;
if (scaffoldKey == null) {
scaffoldKey = GlobalKey<ScaffoldState>();
FletAppServices.of(context).globalKeys["_scaffold"] =
scaffoldKey;
}

WidgetsBinding.instance.addPostFrameCallback((_) {
if (drawerView != null) {
if (drawerView.control.attrBool("open", false)! &&
_drawerOpened != true) {
if (scaffoldKey?.currentState?.isEndDrawerOpen == true) {
scaffoldKey?.currentState?.closeEndDrawer();
}
Future.delayed(const Duration(milliseconds: 1))
.then((value) {
scaffoldKey?.currentState?.openDrawer();
_drawerOpened = true;
});
} else if (!drawerView.control.attrBool("open", false)! &&
_drawerOpened == true) {
scaffoldKey?.currentState?.closeDrawer();
_drawerOpened = false;
}
}
if (endDrawerView != null) {
if (endDrawerView.control.attrBool("open", false)! &&
_endDrawerOpened != true) {
if (scaffoldKey?.currentState?.isDrawerOpen == true) {
scaffoldKey?.currentState?.closeDrawer();
}
Future.delayed(const Duration(milliseconds: 1))
.then((value) {
scaffoldKey?.currentState?.openEndDrawer();
_endDrawerOpened = true;
});
} else if (!endDrawerView.control
.attrBool("open", false)! &&
_endDrawerOpened == true) {
scaffoldKey?.currentState?.closeEndDrawer();
_endDrawerOpened = false;
}
}
});

var scaffold = Scaffold(
key: scaffoldKey,
backgroundColor: HexColor.fromString(
Theme.of(context), control.attrString("bgcolor", "")!),
appBar: appBarView != null
Expand All @@ -685,6 +746,56 @@ class _PageControlState extends State<PageControl> {
height: appBarView.control
.attrDouble("toolbarHeight", kToolbarHeight)!)
: null,
drawer: drawerView != null
? NavigationDrawerControl(
control: drawerView.control,
children: drawerView.children,
parentDisabled: control.isDisabled,
dispatch: widget.dispatch,
)
: null,
onDrawerChanged: (opened) {
if (drawerView != null && !opened) {
_drawerOpened = false;
List<Map<String, String>> props = [
{"i": drawerView.control.id, "open": "false"}
];
widget.dispatch(UpdateControlPropsAction(
UpdateControlPropsPayload(props: props)));
FletAppServices.of(context)
.server
.updateControlProps(props: props);
FletAppServices.of(context).server.sendPageEvent(
eventTarget: drawerView.control.id,
eventName: "dismiss",
eventData: "");
}
},
endDrawer: endDrawerView != null
? NavigationDrawerControl(
control: endDrawerView.control,
children: endDrawerView.children,
parentDisabled: control.isDisabled,
dispatch: widget.dispatch,
)
: null,
onEndDrawerChanged: (opened) {
if (endDrawerView != null && !opened) {
_endDrawerOpened = false;
List<Map<String, String>> props = [
{"i": endDrawerView.control.id, "open": "false"}
];
widget.dispatch(UpdateControlPropsAction(
UpdateControlPropsPayload(props: props)));
FletAppServices.of(context)
.server
.updateControlProps(props: props);
FletAppServices.of(context).server.sendPageEvent(
eventTarget: endDrawerView.control.id,
eventName: "dismiss",
eventData: "");
}
},
body: Stack(children: [
SizedBox.expand(
child: Container(
Expand Down
1 change: 1 addition & 0 deletions sdk/python/packages/flet-core/src/flet_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,4 @@
from flet_core.webview import WebView
from flet_core.range_slider import RangeSlider
from flet_core.badge import Badge
from flet_core.navigation_drawer import NavigationDrawer, NavigationDrawerDestination
Loading

0 comments on commit 5cde674

Please sign in to comment.