mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[flutter]: config editor navigation items
This commit is contained in:
@ -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: [
|
||||||
|
@ -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 => "...";
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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,
|
||||||
|
@ -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(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user