move app bar to bottom screen
This commit is contained in:
@@ -40,6 +40,8 @@ class KagiScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final menuController = useMemoized(() => MenuController());
|
||||||
|
|
||||||
final lastBackButtonPress = useRef<DateTime?>(null);
|
final lastBackButtonPress = useRef<DateTime?>(null);
|
||||||
final webViewController = useRef<InAppWebViewController?>(null);
|
final webViewController = useRef<InAppWebViewController?>(null);
|
||||||
|
|
||||||
@@ -67,172 +69,180 @@ class KagiScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
bottomNavigationBar: BottomAppBar(
|
||||||
automaticallyImplyLeading: false,
|
height: AppBar().preferredSize.height,
|
||||||
title: AppBarTitle(
|
padding: EdgeInsets.zero,
|
||||||
onTap: () {
|
child: AppBar(
|
||||||
final page = ref.read(webViewTabControllerProvider)?.page.value;
|
automaticallyImplyLeading: false,
|
||||||
|
title: AppBarTitle(
|
||||||
if (page != null) {
|
|
||||||
ref.watch(overlayDialogProvider.notifier).show(
|
|
||||||
WebPageDialog(
|
|
||||||
page: page,
|
|
||||||
webViewController: webViewController.value,
|
|
||||||
onDismiss:
|
|
||||||
ref.watch(overlayDialogProvider.notifier).dismiss,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TabsActionButton(
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (displayedSheet case ViewTabs()) {
|
final page = ref.read(webViewTabControllerProvider)?.page.value;
|
||||||
ref.read(bottomSheetProvider.notifier).dismiss();
|
|
||||||
} else {
|
if (page != null) {
|
||||||
ref.read(bottomSheetProvider.notifier).show(ViewTabs());
|
ref.watch(overlayDialogProvider.notifier).show(
|
||||||
|
WebPageDialog(
|
||||||
|
page: page,
|
||||||
|
webViewController: webViewController.value,
|
||||||
|
onDismiss:
|
||||||
|
ref.watch(overlayDialogProvider.notifier).dismiss,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuAnchor(
|
actions: [
|
||||||
builder: (context, controller, child) {
|
TabsActionButton(
|
||||||
return IconButton(
|
onTap: () {
|
||||||
onPressed: () {
|
if (displayedSheet case ViewTabs()) {
|
||||||
if (controller.isOpen) {
|
ref.read(bottomSheetProvider.notifier).dismiss();
|
||||||
controller.close();
|
} else {
|
||||||
} else {
|
ref.read(bottomSheetProvider.notifier).show(ViewTabs());
|
||||||
controller.open();
|
}
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
icon: const Icon(Icons.more_vert),
|
MenuAnchor(
|
||||||
);
|
controller: menuController,
|
||||||
},
|
builder: (context, controller, child) {
|
||||||
menuChildren: [
|
return IconButton(
|
||||||
HookConsumer(
|
onPressed: () {
|
||||||
builder: (context, ref, child) {
|
if (controller.isOpen) {
|
||||||
final activeWebView = ref.watch(webViewTabControllerProvider);
|
controller.close();
|
||||||
final history = useListenableSelector(
|
} else {
|
||||||
activeWebView?.page,
|
controller.open();
|
||||||
() =>
|
}
|
||||||
activeWebView?.page.value.pageHistory ??
|
},
|
||||||
(canGoBack: false, canGoForward: false),
|
icon: const Icon(Icons.more_vert),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
return Row(
|
menuChildren: [
|
||||||
children: [
|
MenuItemButton(
|
||||||
Expanded(
|
onPressed: () async {
|
||||||
child: IconButton(
|
await context.push(AboutRoute().location);
|
||||||
onPressed: (history.canGoBack)
|
},
|
||||||
? () async {
|
leadingIcon: const Icon(Icons.info),
|
||||||
await webViewController.value?.goBack();
|
child: const Text('About'),
|
||||||
}
|
),
|
||||||
: null,
|
const Divider(),
|
||||||
icon: const Icon(Icons.arrow_back),
|
MenuItemButton(
|
||||||
),
|
onPressed: () async {
|
||||||
),
|
await context.push(SettingsRoute().location);
|
||||||
const SizedBox(height: 48, child: VerticalDivider()),
|
},
|
||||||
Expanded(
|
leadingIcon: const Icon(Icons.settings),
|
||||||
child: IconButton(
|
child: const Text('Settings'),
|
||||||
onPressed: (history.canGoForward)
|
),
|
||||||
? () async {
|
if (showEarlyAccessFeatures) const Divider(),
|
||||||
await webViewController.value?.goForward();
|
if (showEarlyAccessFeatures)
|
||||||
}
|
MenuItemButton(
|
||||||
: null,
|
onPressed: () async {
|
||||||
icon: const Icon(Icons.arrow_forward),
|
await context.push(ChatArchiveListRoute().location);
|
||||||
),
|
},
|
||||||
),
|
leadingIcon: const Icon(MdiIcons.archive),
|
||||||
],
|
child: const Text('Chat Archive'),
|
||||||
);
|
),
|
||||||
},
|
const Divider(),
|
||||||
),
|
if (showEarlyAccessFeatures)
|
||||||
const Divider(),
|
MenuItemButton(
|
||||||
MenuItemButton(
|
onPressed: () {
|
||||||
onPressed: () async {
|
ref.read(createTabStreamProvider.notifier).createTab(
|
||||||
await webViewController.value?.reload();
|
CreateTab(preferredTool: KagiTool.assistant),
|
||||||
},
|
);
|
||||||
leadingIcon: const Icon(Icons.refresh),
|
},
|
||||||
child: const Text('Reload'),
|
leadingIcon: const Icon(MdiIcons.brain),
|
||||||
),
|
child: const Text('Assistant'),
|
||||||
const Divider(),
|
),
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await context.push(SettingsRoute().location);
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.settings),
|
|
||||||
child: const Text('Settings'),
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final url = await webViewController.value?.getUrl();
|
|
||||||
if (url != null) {
|
|
||||||
await Share.shareUri(url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.share),
|
|
||||||
child: const Text('Share'),
|
|
||||||
),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final url = await webViewController.value?.getUrl();
|
|
||||||
if (url != null) {
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
await ui_helper.launchUrlFeedback(context, url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.open_in_browser),
|
|
||||||
child: const Text('Launch External'),
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () {
|
|
||||||
ref.read(createTabStreamProvider.notifier).createTab(
|
|
||||||
CreateTab(preferredTool: KagiTool.search),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(MdiIcons.searchWeb),
|
|
||||||
child: const Text('Search'),
|
|
||||||
),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () {
|
|
||||||
ref.read(createTabStreamProvider.notifier).createTab(
|
|
||||||
CreateTab(preferredTool: KagiTool.summarizer),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(MdiIcons.text),
|
|
||||||
child: const Text('Summarizer'),
|
|
||||||
),
|
|
||||||
if (showEarlyAccessFeatures)
|
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(createTabStreamProvider.notifier).createTab(
|
ref.read(createTabStreamProvider.notifier).createTab(
|
||||||
CreateTab(preferredTool: KagiTool.assistant),
|
CreateTab(preferredTool: KagiTool.summarizer),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
leadingIcon: const Icon(MdiIcons.brain),
|
leadingIcon: const Icon(MdiIcons.text),
|
||||||
child: const Text('Assistant'),
|
child: const Text('Summarizer'),
|
||||||
),
|
),
|
||||||
const Divider(),
|
MenuItemButton(
|
||||||
if (showEarlyAccessFeatures)
|
onPressed: () {
|
||||||
|
ref.read(createTabStreamProvider.notifier).createTab(
|
||||||
|
CreateTab(preferredTool: KagiTool.search),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(MdiIcons.searchWeb),
|
||||||
|
child: const Text('Search'),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await context.push(ChatArchiveListRoute().location);
|
final url = await webViewController.value?.getUrl();
|
||||||
|
if (url != null) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
await ui_helper.launchUrlFeedback(context, url);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
leadingIcon: const Icon(MdiIcons.archive),
|
leadingIcon: const Icon(Icons.open_in_browser),
|
||||||
child: const Text('Chat Archive'),
|
child: const Text('Launch External'),
|
||||||
),
|
),
|
||||||
if (showEarlyAccessFeatures) const Divider(),
|
MenuItemButton(
|
||||||
MenuItemButton(
|
onPressed: () async {
|
||||||
onPressed: () async {
|
final url = await webViewController.value?.getUrl();
|
||||||
await context.push(AboutRoute().location);
|
if (url != null) {
|
||||||
},
|
await Share.shareUri(url);
|
||||||
leadingIcon: const Icon(Icons.info),
|
}
|
||||||
child: const Text('About'),
|
},
|
||||||
),
|
leadingIcon: const Icon(Icons.share),
|
||||||
],
|
child: const Text('Share'),
|
||||||
),
|
),
|
||||||
],
|
const Divider(),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await webViewController.value?.reload();
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(Icons.refresh),
|
||||||
|
child: const Text('Reload'),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
HookConsumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final activeWebView =
|
||||||
|
ref.watch(webViewTabControllerProvider);
|
||||||
|
final history = useListenableSelector(
|
||||||
|
activeWebView?.page,
|
||||||
|
() =>
|
||||||
|
activeWebView?.page.value.pageHistory ??
|
||||||
|
(canGoBack: false, canGoForward: false),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: (history.canGoBack)
|
||||||
|
? () async {
|
||||||
|
await webViewController.value?.goBack();
|
||||||
|
menuController.close();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48, child: VerticalDivider()),
|
||||||
|
Expanded(
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: (history.canGoForward)
|
||||||
|
? () async {
|
||||||
|
await webViewController.value?.goForward();
|
||||||
|
menuController.close();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: OverlayPortal(
|
body: OverlayPortal(
|
||||||
controller: overlayController,
|
controller: overlayController,
|
||||||
@@ -305,29 +315,31 @@ class KagiScreen extends HookConsumerWidget {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: AnimatedIndexedStack(
|
child: SafeArea(
|
||||||
duration: const Duration(milliseconds: 250),
|
child: AnimatedIndexedStack(
|
||||||
transitionBuilder: (child, animation, secondaryAnimation) =>
|
duration: const Duration(milliseconds: 250),
|
||||||
SharedAxisTransition(
|
transitionBuilder: (child, animation, secondaryAnimation) =>
|
||||||
animation: animation,
|
SharedAxisTransition(
|
||||||
secondaryAnimation: secondaryAnimation,
|
animation: animation,
|
||||||
transitionType: SharedAxisTransitionType.horizontal,
|
secondaryAnimation: secondaryAnimation,
|
||||||
child: child,
|
transitionType: SharedAxisTransitionType.horizontal,
|
||||||
),
|
child: child,
|
||||||
key: ValueKey(
|
),
|
||||||
(activeWebView != null)
|
key: ValueKey(
|
||||||
? webViews.keys
|
(activeWebView != null)
|
||||||
.toList()
|
? webViews.keys
|
||||||
.indexOf(activeWebView.page.value.key)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
index: (activeWebView != null)
|
|
||||||
? webViews.keys
|
|
||||||
.toList()
|
.toList()
|
||||||
.indexOf(activeWebView.page.value.key) +
|
.indexOf(activeWebView.page.value.key)
|
||||||
1
|
: null,
|
||||||
: 0,
|
),
|
||||||
children: [const LandingContent(), ...webViews.values],
|
index: (activeWebView != null)
|
||||||
|
? webViews.keys
|
||||||
|
.toList()
|
||||||
|
.indexOf(activeWebView.page.value.key) +
|
||||||
|
1
|
||||||
|
: 0,
|
||||||
|
children: [const LandingContent(), ...webViews.values],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -373,17 +373,22 @@ class _WebViewState extends ConsumerState<WebView> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
HookBuilder(
|
Positioned(
|
||||||
builder: (context) {
|
bottom: 0,
|
||||||
final value = useValueListenable(webViewProgress);
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: HookBuilder(
|
||||||
|
builder: (context) {
|
||||||
|
final value = useValueListenable(webViewProgress);
|
||||||
|
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: value < 100,
|
visible: value < 100,
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: value / 100,
|
value: value / 100,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user