mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[flutter]: config home screen question bubble
This commit is contained in:
parent
7246f597af
commit
f1251e7152
@ -2,9 +2,14 @@ import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class QuestionBubble extends StatelessWidget {
|
||||
const QuestionBubble({Key? key}) : super(key: key);
|
||||
@ -17,22 +22,45 @@ class QuestionBubble extends StatelessWidget {
|
||||
height: 30,
|
||||
child: FlowyTextButton(
|
||||
'?',
|
||||
tooltip: QuestionBubbleAction.values.map((action) => action.name).toList().join(','),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
fillColor: theme.selector,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
radius: BorderRadius.circular(10),
|
||||
onPressed: () {
|
||||
final actionList = QuestionBubbleActions(onSelected: (action) {});
|
||||
final actionList = QuestionBubbleActions(onSelected: (result) {
|
||||
result.fold(() {}, (action) {
|
||||
switch (action) {
|
||||
case QuestionBubbleAction.whatsNews:
|
||||
// TODO: annie replace the URL with real ones
|
||||
_launchURL("https://www.google.com");
|
||||
break;
|
||||
case QuestionBubbleAction.help:
|
||||
// TODO: annie replace the URL with real ones
|
||||
_launchURL("https://www.google.com");
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
actionList.show(
|
||||
context,
|
||||
context,
|
||||
anchorDirection: AnchorDirection.topWithCenterAligned,
|
||||
anchorDirection: AnchorDirection.topWithRightAligned,
|
||||
anchorOffset: const Offset(0, -10),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QuestionBubbleActions with ActionList<QuestionBubbleActionWrapper> implements FlowyOverlayDelegate {
|
||||
@ -43,6 +71,12 @@ class QuestionBubbleActions with ActionList<QuestionBubbleActionWrapper> impleme
|
||||
required this.onSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
double get maxWidth => 170;
|
||||
|
||||
@override
|
||||
double get itemHeight => 22;
|
||||
|
||||
@override
|
||||
List<QuestionBubbleActionWrapper> get items => _items;
|
||||
|
||||
@ -63,10 +97,57 @@ class QuestionBubbleActions with ActionList<QuestionBubbleActionWrapper> impleme
|
||||
void didRemove() {
|
||||
onSelected(dartz.none());
|
||||
}
|
||||
|
||||
@override
|
||||
ListOverlayFooter? get footer => ListOverlayFooter(
|
||||
widget: const FlowyVersionDescription(),
|
||||
height: 30,
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
);
|
||||
}
|
||||
|
||||
class FlowyVersionDescription extends StatelessWidget {
|
||||
const FlowyVersionDescription({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
|
||||
return FutureBuilder(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.hasError) {
|
||||
return FlowyText("Error: ${snapshot.error}", fontSize: 12, color: theme.shader4);
|
||||
}
|
||||
|
||||
PackageInfo packageInfo = snapshot.data;
|
||||
String appName = packageInfo.appName;
|
||||
String version = packageInfo.version;
|
||||
String buildNumber = packageInfo.buildNumber;
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Divider(height: 1, color: theme.shader6, thickness: 1.0),
|
||||
const VSpace(6),
|
||||
FlowyText("$appName $version.$buildNumber", fontSize: 12, color: theme.shader4),
|
||||
],
|
||||
).padding(
|
||||
horizontal: ActionListSizes.itemHPadding + ActionListSizes.padding,
|
||||
);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum QuestionBubbleAction {
|
||||
whatsNews,
|
||||
help,
|
||||
}
|
||||
|
||||
class QuestionBubbleActionWrapper extends ActionItemData {
|
||||
@ -74,7 +155,7 @@ class QuestionBubbleActionWrapper extends ActionItemData {
|
||||
|
||||
QuestionBubbleActionWrapper(this.inner);
|
||||
@override
|
||||
Widget? get icon => null;
|
||||
Widget? get icon => inner.emoji;
|
||||
|
||||
@override
|
||||
String get name => inner.name;
|
||||
@ -85,6 +166,17 @@ extension QuestionBubbleExtension on QuestionBubbleAction {
|
||||
switch (this) {
|
||||
case QuestionBubbleAction.whatsNews:
|
||||
return "What's new";
|
||||
case QuestionBubbleAction.help:
|
||||
return "Help & Support";
|
||||
}
|
||||
}
|
||||
|
||||
Widget get emoji {
|
||||
switch (this) {
|
||||
case QuestionBubbleAction.whatsNews:
|
||||
return const Text('😘', style: TextStyle(fontSize: 16));
|
||||
case QuestionBubbleAction.help:
|
||||
return const Text('💁🏻', style: TextStyle(fontSize: 16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ class ActionList {
|
||||
itemBuilder: (context, index) => items[index],
|
||||
anchorContext: anchorContext,
|
||||
anchorDirection: AnchorDirection.bottomRight,
|
||||
maxWidth: 120,
|
||||
maxHeight: 80,
|
||||
width: 120,
|
||||
height: 80,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class AppDisclosureActions with ActionList<AppDisclosureActionWrapper> implements FlowyOverlayDelegate {
|
||||
final Function(dartz.Option<AppDisclosureAction>) onSelected;
|
||||
@ -32,6 +33,9 @@ class AppDisclosureActions with ActionList<AppDisclosureActionWrapper> implement
|
||||
void didRemove() {
|
||||
onSelected(dartz.none());
|
||||
}
|
||||
|
||||
@override
|
||||
ListOverlayFooter? get footer => null;
|
||||
}
|
||||
|
||||
class AppDisclosureActionWrapper extends ActionItemData {
|
||||
@ -39,7 +43,7 @@ class AppDisclosureActionWrapper extends ActionItemData {
|
||||
|
||||
AppDisclosureActionWrapper(this.inner);
|
||||
@override
|
||||
Widget get icon => inner.icon;
|
||||
Widget? get icon => inner.icon;
|
||||
|
||||
@override
|
||||
String get name => inner.name;
|
||||
|
@ -5,6 +5,7 @@ import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
// [[Widget: LifeCycle]]
|
||||
// https://flutterbyexample.com/lesson/stateful-widget-lifecycle
|
||||
@ -37,9 +38,6 @@ class ViewDisclosureButton extends StatelessWidget
|
||||
@override
|
||||
List<ViewDisclosureActionWrapper> get items => _items;
|
||||
|
||||
@override
|
||||
double get maxWidth => 162;
|
||||
|
||||
@override
|
||||
void Function(dartz.Option<ViewDisclosureActionWrapper> p1) get selectCallback => (result) {
|
||||
result.fold(
|
||||
@ -55,6 +53,9 @@ class ViewDisclosureButton extends StatelessWidget
|
||||
void didRemove() {
|
||||
onSelected(dartz.none());
|
||||
}
|
||||
|
||||
@override
|
||||
ListOverlayFooter? get footer => null;
|
||||
}
|
||||
|
||||
class ViewDisclosureActionWrapper extends ActionItemData {
|
||||
@ -62,7 +63,7 @@ class ViewDisclosureActionWrapper extends ActionItemData {
|
||||
|
||||
ViewDisclosureActionWrapper(this.inner);
|
||||
@override
|
||||
Widget get icon => inner.icon;
|
||||
Widget? get icon => inner.icon;
|
||||
|
||||
@override
|
||||
String get name => inner.name;
|
||||
|
@ -15,23 +15,33 @@ abstract class ActionList<T extends ActionItemData> {
|
||||
|
||||
double get maxWidth => 162;
|
||||
|
||||
double get itemHeight => ActionListSizes.itemHeight;
|
||||
|
||||
ListOverlayFooter? get footer;
|
||||
|
||||
void Function(dartz.Option<T>) get selectCallback;
|
||||
|
||||
FlowyOverlayDelegate? get delegate;
|
||||
|
||||
void show(BuildContext buildContext, BuildContext anchorContext,
|
||||
{AnchorDirection anchorDirection = AnchorDirection.bottomRight}) {
|
||||
void show(
|
||||
BuildContext buildContext,
|
||||
BuildContext anchorContext, {
|
||||
AnchorDirection anchorDirection = AnchorDirection.bottomRight,
|
||||
Offset? anchorOffset,
|
||||
}) {
|
||||
final widgets = items
|
||||
.map((action) => ActionItem<T>(
|
||||
.map(
|
||||
(action) => ActionItem<T>(
|
||||
action: action,
|
||||
itemHeight: itemHeight,
|
||||
onSelected: (action) {
|
||||
FlowyOverlay.of(buildContext).remove(identifier);
|
||||
selectCallback(dartz.some(action));
|
||||
}))
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
double totalHeight = widgets.length * (ActionListSizes.itemHeight + ActionListSizes.padding * 2);
|
||||
|
||||
ListOverlay.showWithAnchor(
|
||||
buildContext,
|
||||
identifier: identifier,
|
||||
@ -39,9 +49,11 @@ abstract class ActionList<T extends ActionItemData> {
|
||||
itemBuilder: (context, index) => widgets[index],
|
||||
anchorContext: anchorContext,
|
||||
anchorDirection: anchorDirection,
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: totalHeight,
|
||||
width: maxWidth,
|
||||
height: widgets.length * (itemHeight + ActionListSizes.padding * 2),
|
||||
delegate: delegate,
|
||||
anchorOffset: anchorOffset,
|
||||
footer: footer,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -60,10 +72,12 @@ class ActionListSizes {
|
||||
class ActionItem<T extends ActionItemData> extends StatelessWidget {
|
||||
final T action;
|
||||
final Function(T) onSelected;
|
||||
final double itemHeight;
|
||||
const ActionItem({
|
||||
Key? key,
|
||||
required this.action,
|
||||
required this.onSelected,
|
||||
required this.itemHeight,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -77,7 +91,7 @@ class ActionItem<T extends ActionItemData> extends StatelessWidget {
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => onSelected(action),
|
||||
child: SizedBox(
|
||||
height: ActionListSizes.itemHeight,
|
||||
height: itemHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
if (action.icon != null) action.icon!,
|
||||
|
@ -8,6 +8,7 @@ import Foundation
|
||||
import flowy_editor
|
||||
import flowy_infra_ui
|
||||
import flowy_sdk
|
||||
import package_info_plus_macos
|
||||
import path_provider_macos
|
||||
import url_launcher_macos
|
||||
import window_size
|
||||
@ -16,6 +17,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlowyEditorPlugin.register(with: registry.registrar(forPlugin: "FlowyEditorPlugin"))
|
||||
FlowyInfraUIPlugin.register(with: registry.registrar(forPlugin: "FlowyInfraUIPlugin"))
|
||||
FlowySdkPlugin.register(with: registry.registrar(forPlugin: "FlowySdkPlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin"))
|
||||
|
@ -6,6 +6,8 @@ PODS:
|
||||
- flowy_sdk (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- package_info_plus_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- path_provider_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
@ -18,6 +20,7 @@ DEPENDENCIES:
|
||||
- flowy_infra_ui (from `Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos`)
|
||||
- flowy_sdk (from `Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
|
||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
|
||||
@ -31,6 +34,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
package_info_plus_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
|
||||
path_provider_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||
url_launcher_macos:
|
||||
@ -43,6 +48,7 @@ SPEC CHECKSUMS:
|
||||
flowy_infra_ui: 9d5021b1610fe0476eb1191bf7cd41c4a4138d8f
|
||||
flowy_sdk: c302ac0a22dea596db0df8073b9637b2bf2ff6fd
|
||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
||||
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
|
||||
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
|
||||
url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4
|
||||
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
||||
|
@ -57,7 +57,7 @@
|
||||
1CD81A6C7244B2318E0BA2E8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* app_flowy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app_flowy.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10ED2044A3C60003C045 /* AppFlowy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppFlowy.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@ -112,7 +112,7 @@
|
||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10ED2044A3C60003C045 /* app_flowy.app */,
|
||||
33CC10ED2044A3C60003C045 /* AppFlowy.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -192,7 +192,7 @@
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* app_flowy.app */;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* AppFlowy.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@ -425,6 +425,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_NAME = AppFlowy;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRIP_STYLE = "non-global";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -552,6 +553,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_NAME = AppFlowy;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRIP_STYLE = "non-global";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@ -573,6 +575,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_NAME = AppFlowy;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRIP_STYLE = "non-global";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -15,7 +15,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "app_flowy.app"
|
||||
BuildableName = "AppFlowy.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -31,13 +31,13 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "app_flowy.app"
|
||||
BuildableName = "AppFlowy.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@ -54,13 +54,11 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "app_flowy.app"
|
||||
BuildableName = "AppFlowy.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
@ -73,7 +71,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "app_flowy.app"
|
||||
BuildableName = "AppFlowy.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
@ -188,8 +188,8 @@ class OverlayScreen extends StatelessWidget {
|
||||
anchorContext: buttonContext,
|
||||
anchorDirection: providerContext.read<OverlayDemoConfiguration>().anchorDirection,
|
||||
overlapBehaviour: providerContext.read<OverlayDemoConfiguration>().overlapBehaviour,
|
||||
maxWidth: 200.0,
|
||||
maxHeight: 200.0,
|
||||
width: 200.0,
|
||||
height: 200.0,
|
||||
);
|
||||
},
|
||||
child: const Text('Show List Overlay'),
|
||||
|
@ -163,7 +163,7 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
FlowyOverlayDelegate? delegate,
|
||||
OverlapBehaviour? overlapBehaviour,
|
||||
FlowyOverlayStyle? style,
|
||||
Offset? anchorPosition,
|
||||
Offset? anchorOffset,
|
||||
}) {
|
||||
this.style = style ?? FlowyOverlayStyle();
|
||||
|
||||
@ -175,7 +175,7 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
anchorContext: anchorContext,
|
||||
anchorDirection: anchorDirection,
|
||||
overlapBehaviour: overlapBehaviour,
|
||||
anchorPosition: anchorPosition,
|
||||
anchorOffset: anchorOffset,
|
||||
);
|
||||
}
|
||||
|
||||
@ -209,10 +209,12 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
Size? anchorSize,
|
||||
AnchorDirection? anchorDirection,
|
||||
BuildContext? anchorContext,
|
||||
Offset? anchorOffset,
|
||||
OverlapBehaviour? overlapBehaviour,
|
||||
FlowyOverlayDelegate? delegate,
|
||||
}) {
|
||||
Widget overlay = widget;
|
||||
final offset = anchorOffset ?? Offset.zero;
|
||||
|
||||
if (shouldAnchor) {
|
||||
assert(
|
||||
@ -232,8 +234,8 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
targetAnchorSize = renderBox.size;
|
||||
}
|
||||
final anchorRect = Rect.fromLTWH(
|
||||
targetAnchorPosition.dx,
|
||||
targetAnchorPosition.dy,
|
||||
targetAnchorPosition.dx + offset.dx,
|
||||
targetAnchorPosition.dy + offset.dy,
|
||||
targetAnchorSize.width,
|
||||
targetAnchorSize.height,
|
||||
);
|
||||
|
@ -2,38 +2,68 @@ import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/decoration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ListOverlayFooter {
|
||||
Widget widget;
|
||||
double height;
|
||||
EdgeInsets padding;
|
||||
ListOverlayFooter({
|
||||
required this.widget,
|
||||
required this.height,
|
||||
this.padding = EdgeInsets.zero,
|
||||
});
|
||||
}
|
||||
|
||||
class ListOverlay extends StatelessWidget {
|
||||
const ListOverlay({
|
||||
Key? key,
|
||||
required this.itemBuilder,
|
||||
this.itemCount,
|
||||
this.controller,
|
||||
this.maxWidth = double.infinity,
|
||||
this.maxHeight = double.infinity,
|
||||
this.width = double.infinity,
|
||||
this.height = double.infinity,
|
||||
this.footer,
|
||||
}) : super(key: key);
|
||||
|
||||
final IndexedWidgetBuilder itemBuilder;
|
||||
final int? itemCount;
|
||||
final ScrollController? controller;
|
||||
final double maxWidth;
|
||||
final double maxHeight;
|
||||
final double width;
|
||||
final double height;
|
||||
final ListOverlayFooter? footer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const padding = EdgeInsets.symmetric(horizontal: 6, vertical: 6);
|
||||
double totalHeight = height + padding.vertical;
|
||||
if (footer != null) {
|
||||
totalHeight = totalHeight + footer!.height + footer!.padding.vertical;
|
||||
}
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Container(
|
||||
constraints: BoxConstraints.tight(Size(maxWidth, maxHeight + padding.vertical)),
|
||||
decoration: FlowyDecoration.decoration(),
|
||||
constraints: BoxConstraints.tight(Size(width, totalHeight)),
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: ListView.builder(
|
||||
child: Column(
|
||||
children: [
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: itemBuilder,
|
||||
itemCount: itemCount,
|
||||
controller: controller,
|
||||
),
|
||||
if (footer != null)
|
||||
SizedBox(
|
||||
height: footer!.height,
|
||||
child: Padding(
|
||||
padding: footer!.padding,
|
||||
child: SizedBox.expand(child: footer!.widget),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -45,27 +75,31 @@ class ListOverlay extends StatelessWidget {
|
||||
required IndexedWidgetBuilder itemBuilder,
|
||||
int? itemCount,
|
||||
ScrollController? controller,
|
||||
double maxWidth = double.infinity,
|
||||
double maxHeight = double.infinity,
|
||||
double width = double.infinity,
|
||||
double height = double.infinity,
|
||||
required BuildContext anchorContext,
|
||||
AnchorDirection? anchorDirection,
|
||||
FlowyOverlayDelegate? delegate,
|
||||
OverlapBehaviour? overlapBehaviour,
|
||||
FlowyOverlayStyle? style,
|
||||
Offset? anchorOffset,
|
||||
ListOverlayFooter? footer,
|
||||
}) {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: ListOverlay(
|
||||
itemBuilder: itemBuilder,
|
||||
itemCount: itemCount,
|
||||
controller: controller,
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
width: width,
|
||||
height: height,
|
||||
footer: footer,
|
||||
),
|
||||
identifier: identifier,
|
||||
anchorContext: anchorContext,
|
||||
anchorDirection: anchorDirection,
|
||||
delegate: delegate,
|
||||
overlapBehaviour: overlapBehaviour,
|
||||
anchorOffset: anchorOffset,
|
||||
style: style,
|
||||
);
|
||||
}
|
||||
@ -91,8 +125,8 @@ class ListOverlay extends StatelessWidget {
|
||||
itemBuilder: itemBuilder,
|
||||
itemCount: itemCount,
|
||||
controller: controller,
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
identifier: identifier,
|
||||
anchorPosition: anchorPosition,
|
||||
|
@ -22,14 +22,14 @@ class OptionOverlay<T> extends StatelessWidget {
|
||||
|
||||
static void showWithAnchor<T>(
|
||||
BuildContext context, {
|
||||
required String identifier,
|
||||
required List<T> items,
|
||||
required String identifier,
|
||||
required BuildContext anchorContext,
|
||||
IndexedValueCallback<T>? onHover,
|
||||
IndexedValueCallback<T>? onTap,
|
||||
required BuildContext anchorContext,
|
||||
AnchorDirection? anchorDirection,
|
||||
FlowyOverlayDelegate? delegate,
|
||||
OverlapBehaviour? overlapBehaviour,
|
||||
FlowyOverlayDelegate? delegate,
|
||||
}) {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OptionOverlay(
|
||||
|
@ -65,6 +65,7 @@ class FlowyTextButton extends StatelessWidget {
|
||||
final Color? fillColor;
|
||||
final BorderRadius? radius;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final String? tooltip;
|
||||
|
||||
// final HoverDisplayConfig? hoverDisplay;
|
||||
const FlowyTextButton(
|
||||
@ -80,6 +81,7 @@ class FlowyTextButton extends StatelessWidget {
|
||||
this.heading,
|
||||
this.radius,
|
||||
this.mainAxisAlignment = MainAxisAlignment.start,
|
||||
this.tooltip,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -108,7 +110,7 @@ class FlowyTextButton extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
return RawMaterialButton(
|
||||
child = RawMaterialButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
hoverElevation: 0,
|
||||
highlightElevation: 0,
|
||||
@ -123,20 +125,14 @@ class FlowyTextButton extends StatelessWidget {
|
||||
child: child,
|
||||
);
|
||||
|
||||
// if (hoverColor != null) {
|
||||
// return InkWell(
|
||||
// onTap: onPressed,
|
||||
// child: FlowyHover(
|
||||
// config: HoverDisplayConfig(borderRadius: radius ?? BorderRadius.circular(6), hoverColor: hoverColor!),
|
||||
// builder: (context, onHover) => child,
|
||||
// ),
|
||||
// );
|
||||
// } else {
|
||||
// return InkWell(
|
||||
// onTap: onPressed,
|
||||
// child: child,
|
||||
// );
|
||||
// }
|
||||
if (tooltip != null) {
|
||||
child = Tooltip(
|
||||
message: tooltip!,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
// return TextButton(
|
||||
|
@ -597,6 +597,48 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
package_info_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
package_info_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
package_info_plus_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
package_info_plus_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -63,6 +63,7 @@ dependencies:
|
||||
expandable: ^5.0.1
|
||||
flutter_svg: ^0.22.0
|
||||
flutter_colorpicker: ^0.6.0
|
||||
package_info_plus: ^1.3.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
Loading…
Reference in New Issue
Block a user