[flutter]: config editor navigation items

This commit is contained in:
appflowy
2021-10-10 17:01:30 +08:00
parent fb1733e188
commit ae82e052f2
7 changed files with 103 additions and 82 deletions

View File

@ -1,16 +1,36 @@
import 'package:app_flowy/workspace/presentation/stack_page/doc/doc_stack_page.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/presentation/stack_page/blank/blank_page.dart'; import 'package:app_flowy/workspace/presentation/stack_page/blank/blank_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/doc/doc_stack_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/fading_index_stack.dart'; import 'package:app_flowy/workspace/presentation/stack_page/fading_index_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/prelude.dart'; import 'package:app_flowy/workspace/presentation/widgets/prelude.dart';
abstract class HomeStackContext extends Equatable { typedef NavigationCallback = void Function(String id);
abstract class NavigationItem {
String get title; String get title;
String get identifier; String get identifier;
NavigationCallback get action => (id) {
getIt<HomeStack>().setStackWithId(id);
};
}
abstract class HomeStackContext extends Equatable with NavigationItem {
List<NavigationItem> get navigationItems;
@override
String get title;
@override
String get identifier;
ViewType get type; ViewType get type;
Widget render(); Widget render();
} }
@ -27,10 +47,7 @@ HomeStackContext stackCtxFromView(View view) {
class HomeStackNotifier extends ChangeNotifier { class HomeStackNotifier extends ChangeNotifier {
HomeStackContext inner; HomeStackContext inner;
HomeStackNotifier({HomeStackContext? context}) : inner = context ?? DefaultHomeStackContext();
HomeStackNotifier({
HomeStackContext? context,
}) : inner = context ?? DefaultHomeStackContext();
set context(HomeStackContext context) { set context(HomeStackContext context) {
inner = context; inner = context;
@ -40,7 +57,7 @@ class HomeStackNotifier extends ChangeNotifier {
HomeStackContext get context => inner; HomeStackContext get context => inner;
} }
// HomePageStack is initialized as singleton to controll the page stack. // HomeStack is initialized as singleton to controll the page stack.
class HomeStack { class HomeStack {
final HomeStackNotifier _notifier = HomeStackNotifier(); final HomeStackNotifier _notifier = HomeStackNotifier();
HomeStack(); HomeStack();
@ -53,6 +70,8 @@ class HomeStack {
_notifier.context = context; _notifier.context = context;
} }
void setStackWithId(String id) {}
Widget stackTopBar() { Widget stackTopBar() {
return MultiProvider( return MultiProvider(
providers: [ providers: [

View File

@ -1,5 +1,4 @@
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart'; import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/home_top_bar.dart';
import 'package:flowy_infra_ui/style_widget/text_button.dart'; import 'package:flowy_infra_ui/style_widget/text_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -7,34 +6,38 @@ import 'package:styled_widget/styled_widget.dart';
typedef NaviAction = void Function(); typedef NaviAction = void Function();
abstract class NaviItem {
String get identifier;
String get title;
NaviAction get action;
}
class NavigationNotifier with ChangeNotifier { class NavigationNotifier with ChangeNotifier {
HomeStackNotifier pageStackNotifier; HomeStackNotifier homeStackNotifier;
NavigationNotifier(this.pageStackNotifier); NavigationNotifier(this.homeStackNotifier);
void update(HomeStackNotifier notifier) { void update(HomeStackNotifier notifier) {
pageStackNotifier = notifier; homeStackNotifier = notifier;
notifyListeners(); notifyListeners();
} }
List<NaviItem> get naviItems { List<NavigationItem> get naviItems => homeStackNotifier.context.navigationItems;
List<NaviItem> items = [
ViewNaviItemImpl(pageStackNotifier.context),
// ViewNaviItemImpl(pageStackNotifier.view),
// ViewNaviItemImpl(pageStackNotifier.view),
// ViewNaviItemImpl(pageStackNotifier.view),
// ViewNaviItemImpl(pageStackNotifier.view),
// ViewNaviItemImpl(pageStackNotifier.view)
];
return items;
}
} }
// [[Navigation]]
// ┌───────────────────────┐
// 2.notify listeners ┌──────│DefaultHomeStackContext│
// ┌────────────────┐ ┌───────────┐ ┌────────────────┐ │ └───────────────────────┘
// │HomeStackNotifie│◀──────────│ HomeStack │◀──│HomeStackContext│◀─ impl
// └────────────────┘ └───────────┘ └────────────────┘ │ ┌───────────────────┐
// │ ▲ └───────│ DocStackContext │
// │ │ └───────────────────┘
// 3.notify change 1.set context
// │ │
// ▼ │
// ┌───────────────────┐ ┌──────────────────┐
// │NavigationNotifier │ │ ViewSectionItem │
// └───────────────────┘ └──────────────────┘
// │
// │
// ▼
// ┌─────────────────┐
// │ FlowyNavigation │ 4.render navigation items
// └─────────────────┘
class FlowyNavigation extends StatelessWidget { class FlowyNavigation extends StatelessWidget {
const FlowyNavigation({Key? key}) : super(key: key); const FlowyNavigation({Key? key}) : super(key: key);
@ -42,10 +45,7 @@ class FlowyNavigation extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<HomeStackNotifier, NavigationNotifier>( return ChangeNotifierProxyProvider<HomeStackNotifier, NavigationNotifier>(
create: (_) => NavigationNotifier( create: (_) => NavigationNotifier(
Provider.of<HomeStackNotifier>( Provider.of<HomeStackNotifier>(context, listen: false),
context,
listen: false,
),
), ),
update: (_, notifier, controller) => controller!..update(notifier), update: (_, notifier, controller) => controller!..update(notifier),
child: Consumer(builder: (ctx, NavigationNotifier notifier, child) { child: Consumer(builder: (ctx, NavigationNotifier notifier, child) {
@ -54,12 +54,12 @@ class FlowyNavigation extends StatelessWidget {
); );
} }
List<Widget> _renderChildren(List<NaviItem> items) { List<Widget> _renderChildren(List<NavigationItem> items) {
if (items.isEmpty) { if (items.isEmpty) {
return []; return [];
} }
List<NaviItem> newItems = _filter(items); List<NavigationItem> newItems = _filter(items);
Widget last = NaviItemWidget(newItems.removeLast()); Widget last = NaviItemWidget(newItems.removeLast());
List<Widget> widgets = List.empty(growable: true); List<Widget> widgets = List.empty(growable: true);
@ -69,7 +69,7 @@ class FlowyNavigation extends StatelessWidget {
return widgets; return widgets;
} }
List<NaviItem> _filter(List<NaviItem> items) { List<NavigationItem> _filter(List<NavigationItem> items) {
final length = items.length; final length = items.length;
if (length > 4) { if (length > 4) {
final first = items[0]; final first = items[0];
@ -87,16 +87,17 @@ class FlowyNavigation extends StatelessWidget {
} }
class NaviItemWidget extends StatelessWidget { class NaviItemWidget extends StatelessWidget {
final NaviItem item; final NavigationItem item;
const NaviItemWidget(this.item, {Key? key}) : super(key: key); const NaviItemWidget(this.item, {Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
height: 30, height: 24,
child: FlowyTextButton( child: FlowyTextButton(
item.title, item.title,
fontSize: 14, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
fontSize: 12,
onPressed: () { onPressed: () {
debugPrint('show app document'); debugPrint('show app document');
}, },
@ -117,18 +118,18 @@ class NaviItemDivider extends StatelessWidget {
} }
} }
class EllipsisNaviItem extends NaviItem { class EllipsisNaviItem extends NavigationItem {
final List<NaviItem> items; final List<NavigationItem> items;
EllipsisNaviItem({ EllipsisNaviItem({
required this.items, required this.items,
}); });
@override @override
NaviAction get action => throw UnimplementedError(); String get title => "...";
@override
NavigationCallback get action => (id) {};
@override @override
String get identifier => "Ellipsis"; String get identifier => "Ellipsis";
@override
String get title => "...";
} }

View File

@ -19,6 +19,9 @@ class DefaultHomeStackContext extends HomeStackContext {
Widget render() { Widget render() {
return const AnnouncementStackPage(); return const AnnouncementStackPage();
} }
@override
List<NavigationItem> get navigationItems => [this];
} }
class AnnouncementStackPage extends StatefulWidget { class AnnouncementStackPage extends StatefulWidget {

View File

@ -28,6 +28,21 @@ class DocStackContext extends HomeStackContext {
Widget render() { Widget render() {
return DocStackPage(_view, key: ValueKey(_view.id)); return DocStackPage(_view, key: ValueKey(_view.id));
} }
@override
List<NavigationItem> get navigationItems => makeNavigationItems();
// List<NavigationItem> get navigationItems => naviStacks.map((stack) {
// return NavigationItemImpl(context: stack);
// }).toList();
List<NavigationItem> makeNavigationItems() {
return [
this,
this,
this,
];
}
} }
class DocStackPage extends StatefulWidget { class DocStackPage extends StatefulWidget {

View File

@ -1,4 +1,3 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/domain/image.dart'; import 'package:app_flowy/workspace/domain/image.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart'; import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
@ -86,18 +85,3 @@ class HomeTitle extends StatelessWidget {
); );
} }
} }
class ViewNaviItemImpl extends NaviItem {
final HomeStackContext view;
ViewNaviItemImpl(this.view);
@override
NaviAction get action => () => getIt<HomeStack>().setStack(view);
@override
String get identifier => view.identifier;
@override
String get title => view.title;
}

View File

@ -11,7 +11,7 @@ class FlowyHover extends StatefulWidget {
const FlowyHover({ const FlowyHover({
Key? key, Key? key,
required this.builder, required this.builder,
this.config = const HoverDisplayConfig(), required this.config,
}) : super(key: key); }) : super(key: key);
@override @override
@ -23,40 +23,39 @@ class _FlowyHoverState extends State<FlowyHover> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hoverColor = _onHover ? widget.config.hoverColor : Theme.of(context).colorScheme.background;
final config = widget.config.copyWith(hoverColor: hoverColor);
return MouseRegion( return MouseRegion(
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
onEnter: (p) => setOnHover(true), onEnter: (p) => setOnHover(true),
onExit: (p) => setOnHover(false), onExit: (p) => setOnHover(false),
child: FlowyHoverBackground(config: config, child: widget.builder(context, _onHover)), child: render(),
); );
} }
void setOnHover(bool value) => setState(() => _onHover = value); void setOnHover(bool value) => setState(() => _onHover = value);
Widget render() {
if (_onHover) {
return FlowyHoverBackground(
config: widget.config,
child: widget.builder(context, _onHover),
);
} else {
return widget.builder(context, _onHover);
}
}
} }
class HoverDisplayConfig { class HoverDisplayConfig {
final Color borderColor; final Color borderColor;
final double borderWidth; final double borderWidth;
final Color? hoverColor; final Color hoverColor;
final BorderRadius borderRadius; final BorderRadius borderRadius;
const HoverDisplayConfig( const HoverDisplayConfig(
{this.borderColor = Colors.transparent, {this.borderColor = Colors.transparent,
this.borderWidth = 0, this.borderWidth = 0,
this.borderRadius = const BorderRadius.all(Radius.circular(6)), this.borderRadius = const BorderRadius.all(Radius.circular(6)),
this.hoverColor}); required this.hoverColor});
HoverDisplayConfig copyWith({Color? hoverColor}) {
return HoverDisplayConfig(
borderColor: borderColor,
borderWidth: borderWidth,
borderRadius: borderRadius,
hoverColor: hoverColor,
);
}
} }
class FlowyHoverBackground extends StatelessWidget { class FlowyHoverBackground extends StatelessWidget {
@ -67,12 +66,11 @@ class FlowyHoverBackground extends StatelessWidget {
const FlowyHoverBackground({ const FlowyHoverBackground({
Key? key, Key? key,
required this.child, required this.child,
this.config = const HoverDisplayConfig(), required this.config,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final color = config.hoverColor ?? Theme.of(context).colorScheme.background;
final hoverBorder = Border.all( final hoverBorder = Border.all(
color: config.borderColor, color: config.borderColor,
width: config.borderWidth, width: config.borderWidth,
@ -81,7 +79,7 @@ class FlowyHoverBackground extends StatelessWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: hoverBorder, border: hoverBorder,
color: color, color: config.hoverColor,
borderRadius: config.borderRadius, borderRadius: config.borderRadius,
), ),
child: child, child: child,

View File

@ -1,7 +1,9 @@
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class FlowyTextButton extends StatelessWidget { class FlowyTextButton extends StatelessWidget {
final String text; final String text;
@ -17,12 +19,11 @@ class FlowyTextButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return InkWell( return InkWell(
onTap: onPressed, onTap: onPressed,
child: FlowyHover( child: FlowyHover(
config: HoverDisplayConfig( config: HoverDisplayConfig(borderRadius: BorderRadius.circular(6), hoverColor: theme.bg3),
borderRadius: BorderRadius.circular(8),
hoverColor: Colors.grey.shade300),
builder: (context, onHover) => _render(), builder: (context, onHover) => _render(),
), ),
); );