update onboarding; add missing safe areas

This commit is contained in:
Fabian Freund
2026-03-17 08:46:09 +01:00
parent 79b68f137e
commit 25f4d27f0d
49 changed files with 3938 additions and 2430 deletions
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
}
}
String _$tabRepositoryHash() => r'87d2a4a0bca93c2d925aa1c81ff80d443185950a';
String _$tabRepositoryHash() => r'ce4ac2f2efbb859ba66e98c6e6d21f8205d9b4ed';
abstract class _$TabRepository extends $Notifier<void> {
void build();
@@ -99,69 +99,71 @@ class BookmarkFolderEditScreen extends HookConsumerWidget {
),
],
),
body: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: ListView(
children: [
TextFormField(
controller: nameTextController,
decoration: const InputDecoration(
label: Text('Name'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
validator: validateRequired,
),
const SizedBox(height: 16),
if (!isBookmarkRoot) ...[
FolderTreePicker(
selectedFolderGuid: currentParentGuid,
excludeFolderGuids: folder != null
? {folder!.guid}
: const {},
entryGuid: BookmarkRoot.root.id,
),
if (folder == null) ...[
const SizedBox(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Add to top'),
value: addToTop.value,
onChanged: (value) => addToTop.value = value,
body: SafeArea(
child: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: ListView(
children: [
TextFormField(
controller: nameTextController,
decoration: const InputDecoration(
label: Text('Name'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
],
validator: validateRequired,
),
const SizedBox(height: 16),
],
if (folder != null)
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
side: BorderSide(
color: Theme.of(context).colorScheme.error,
),
foregroundColor: Theme.of(context).colorScheme.error,
iconColor: Theme.of(context).colorScheme.error,
),
label: const Text('Delete'),
icon: const Icon(Icons.delete),
onPressed: () async {
final result = await showDeleteFolderDialog(context);
if (result == true) {
await ref
.read(bookmarksRepositoryProvider.notifier)
.delete(folder!.guid);
if (context.mounted) {
context.pop();
}
}
},
if (!isBookmarkRoot) ...[
FolderTreePicker(
selectedFolderGuid: currentParentGuid,
excludeFolderGuids: folder != null
? {folder!.guid}
: const {},
entryGuid: BookmarkRoot.root.id,
),
),
],
if (folder == null) ...[
const SizedBox(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Add to top'),
value: addToTop.value,
onChanged: (value) => addToTop.value = value,
),
],
const SizedBox(height: 16),
],
if (folder != null)
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
side: BorderSide(
color: Theme.of(context).colorScheme.error,
),
foregroundColor: Theme.of(context).colorScheme.error,
iconColor: Theme.of(context).colorScheme.error,
),
label: const Text('Delete'),
icon: const Icon(Icons.delete),
onPressed: () async {
final result = await showDeleteFolderDialog(context);
if (result == true) {
await ref
.read(bookmarksRepositoryProvider.notifier)
.delete(folder!.guid);
if (context.mounted) {
context.pop();
}
}
},
),
),
],
),
),
),
),
@@ -1,6 +1,3 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_svg/svg.dart';
@@ -15,20 +12,11 @@ import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors
import 'package:weblibre/features/quotes/data/database/definitions.drift.dart';
import 'package:weblibre/features/quotes/domain/providers.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/widgets/browser_page.dart';
class BrowserHome extends ConsumerWidget {
const BrowserHome({super.key});
static const _brandPurple = Color(0xFF9C83F8);
static const _brandYellow = Color(0xFFFBDC6B);
static const _brandGrey = Color(0xFFA7A7A7);
static const _auraPurple = Color(0xFF2C2543);
static const _auraGold = Color(0xFF3C3827);
static const _auraShadow = Color(0xFF222224);
static const _auraShadowHighlight = Color(0xFF2D2D31);
static const _auraTint = Color(0xFF000000);
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
@@ -80,286 +68,152 @@ class BrowserHome extends ConsumerWidget {
.resumeLatestContainerTab(containerId);
}
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color.alphaBlend(
_auraPurple.withValues(alpha: 0.38),
colorScheme.surfaceContainerLowest,
),
Color.alphaBlend(
_auraShadow.withValues(alpha: 0.72),
colorScheme.surface,
),
Color.alphaBlend(
_auraGold.withValues(alpha: 0.34),
colorScheme.surfaceContainerHigh,
),
],
),
),
child: Stack(
fit: StackFit.expand,
return BrowserPage(
child: BrowserPageContent(
bottomViewportInset: bottomViewportInset,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
const Positioned(
top: -70,
left: -120,
child: _BackdropOrb(width: 400, height: 400, color: _auraPurple),
),
const Positioned(
top: 220,
right: -150,
child: _BackdropOrb(width: 340, height: 340, color: _auraGold),
),
const Positioned(
bottom: 18,
left: -8,
child: _BackdropOrb(
width: 320,
height: 320,
color: _auraShadowHighlight,
if (containerData != null)
_ContainerHeader(container: containerData)
else
BrandHeader(colorScheme: colorScheme),
const SizedBox(height: 24),
if (!hasTabs) ...[
Text(
'WebLibre is ready',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
Positioned.fill(
child: IgnorePointer(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 72, sigmaY: 72),
child: ColoredBox(color: _auraTint.withValues(alpha: 0.12)),
const SizedBox(height: 10),
Text(
'A browser that respects you. Open a tab and experience the web, libre.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
] else if (containerData != null) ...[
Text(
containerData.name?.isNotEmpty == true
? containerData.name!
: 'Container',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
hasContainerTabs
? 'No matching tab selected'
: 'No open tabs in this container',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
],
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: Color.alphaBlend(
BrowserPage.brandGrey.withValues(alpha: 0.18),
colorScheme.outlineVariant,
),
),
),
),
LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.fromLTRB(
24,
32,
24,
32 + bottomViewportInset,
),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: math.max(
0,
constraints.maxHeight - 64 - bottomViewportInset,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(12),
),
child: const Icon(MdiIcons.formatQuoteOpen, size: 18),
),
),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (containerData != null)
_ContainerHeader(container: containerData)
else
_BrandHeader(colorScheme: colorScheme),
const SizedBox(height: 24),
if (!hasTabs) ...[
Text(
'WebLibre is ready',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
'A browser that respects you. Open a tab and experience the web, libre.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
] else if (containerData != null) ...[
Text(
containerData.name?.isNotEmpty == true
? containerData.name!
: 'Container',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
hasContainerTabs
? 'No matching tab selected'
: 'No open tabs in this container',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
],
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer.withValues(
alpha: 0.9,
),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: Color.alphaBlend(
_brandGrey.withValues(alpha: 0.18),
colorScheme.outlineVariant,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme
.colorScheme
.surfaceContainerHigh,
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
MdiIcons.formatQuoteOpen,
size: 18,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'A thought for the road',
style: theme.textTheme.titleMedium
?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
IconButton.filledTonal(
tooltip: 'Refresh quote',
onPressed: () {
ref.invalidate(randomQuoteProvider);
},
icon: const Icon(Icons.refresh_rounded),
),
],
),
const SizedBox(height: 16),
switch (quoteAsync) {
AsyncData(:final value) => _QuoteBlock(
quote: value,
),
AsyncError() => Text(
'Open a new tab and make this space your own.',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.5,
),
),
_ => const LinearProgressIndicator(
minHeight: 3,
),
},
],
),
),
const SizedBox(height: 24),
Wrap(
alignment: WrapAlignment.center,
spacing: 12,
runSpacing: 12,
children: [
if (hasTabs)
OutlinedButton.icon(
onPressed: viewTabs,
icon: const Icon(Icons.tab_rounded),
label: const Text('View tabs'),
),
FilledButton.icon(
onPressed: openNewTab,
icon: const Icon(Icons.add_rounded),
label: const Text('New tab'),
),
if (hasContainerTabs)
FilledButton.tonalIcon(
onPressed: resumeLatestContainerTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
)
else if (hasTabs && containerData == null)
FilledButton.tonalIcon(
onPressed: resumeLatestTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
),
],
),
],
const SizedBox(width: 12),
Expanded(
child: Text(
'A thought for the road',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
),
IconButton.filledTonal(
tooltip: 'Refresh quote',
onPressed: () {
ref.invalidate(randomQuoteProvider);
},
icon: const Icon(Icons.refresh_rounded),
),
],
),
);
},
const SizedBox(height: 16),
switch (quoteAsync) {
AsyncData(:final value) => _QuoteBlock(quote: value),
AsyncError() => Text(
'Open a new tab and make this space your own.',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.5,
),
),
_ => const LinearProgressIndicator(minHeight: 3),
},
],
),
),
const SizedBox(height: 24),
Wrap(
alignment: WrapAlignment.center,
spacing: 12,
runSpacing: 12,
children: [
if (hasTabs)
OutlinedButton.icon(
onPressed: viewTabs,
icon: const Icon(Icons.tab_rounded),
label: const Text('View tabs'),
),
FilledButton.icon(
onPressed: openNewTab,
icon: const Icon(Icons.add_rounded),
label: const Text('New tab'),
),
if (hasContainerTabs)
FilledButton.tonalIcon(
onPressed: resumeLatestContainerTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
)
else if (hasTabs && containerData == null)
FilledButton.tonalIcon(
onPressed: resumeLatestTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
),
],
),
],
),
);
}
}
class _BrandHeader extends StatelessWidget {
final ColorScheme colorScheme;
const _BrandHeader({required this.colorScheme});
@override
Widget build(BuildContext context) {
return Container(
width: 112,
height: 112,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color.alphaBlend(
BrowserHome._brandPurple.withValues(alpha: 0.18),
colorScheme.surfaceContainerHighest,
),
Color.alphaBlend(
BrowserHome._brandYellow.withValues(alpha: 0.12),
colorScheme.surfaceContainer,
),
],
),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.45),
),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.08),
blurRadius: 32,
offset: const Offset(0, 18),
),
],
),
child: Center(
child: SvgPicture.asset('assets/icon/icon.svg', width: 72, height: 72),
),
);
}
@@ -467,26 +321,3 @@ class _QuoteBlock extends StatelessWidget {
);
}
}
class _BackdropOrb extends StatelessWidget {
final double width;
final double height;
final Color color;
const _BackdropOrb({
required this.width,
required this.height,
required this.color,
});
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: Container(
width: width,
height: height,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
),
);
}
}
@@ -44,131 +44,135 @@ class ContainerListScreen extends HookConsumerWidget {
return Scaffold(
appBar: AppBar(title: const Text('Containers')),
body: Skeletonizer(
enabled: containersAsync.isLoading,
child: containersAsync.when(
skipLoadingOnReload: true,
data: (containers) => FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return ListView.builder(
controller: controller,
itemCount: containers.length,
itemBuilder: (context, index) {
final container = containers[index];
return Slidable(
key: ValueKey(container.id),
startActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
if (container.id != selectedContainer)
SlidableAction(
onPressed: (context) async {
final result = await ref
.read(selectedContainerProvider.notifier)
.setContainerId(container.id);
body: SafeArea(
child: Skeletonizer(
enabled: containersAsync.isLoading,
child: containersAsync.when(
skipLoadingOnReload: true,
data: (containers) => FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return ListView.builder(
controller: controller,
itemCount: containers.length,
itemBuilder: (context, index) {
final container = containers[index];
return Slidable(
key: ValueKey(container.id),
startActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
if (container.id != selectedContainer)
SlidableAction(
onPressed: (context) async {
final result = await ref
.read(selectedContainerProvider.notifier)
.setContainerId(container.id);
if (context.mounted &&
result ==
SetContainerResult.successHasProxy) {
final shouldStartProxy = await ref
.read(startProxyControllerProvider.notifier)
.shouldPromptProxyStart();
if (!context.mounted || !shouldStartProxy) {
return;
}
final dialogResult = await showDialog<bool>(
context: context,
builder: (context) {
return const TorDialog();
},
);
if (dialogResult == true) {
await ref
if (context.mounted &&
result ==
SetContainerResult.successHasProxy) {
final shouldStartProxy = await ref
.read(
startProxyControllerProvider.notifier,
)
.startProxy();
.shouldPromptProxyStart();
if (!context.mounted || !shouldStartProxy) {
return;
}
final dialogResult = await showDialog<bool>(
context: context,
builder: (context) {
return const TorDialog();
},
);
if (dialogResult == true) {
await ref
.read(
startProxyControllerProvider.notifier,
)
.startProxy();
}
}
}
},
foregroundColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
backgroundColor: Theme.of(
context,
).colorScheme.primaryContainer,
icon: Icons.check,
label: 'Select',
)
else
},
foregroundColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
backgroundColor: Theme.of(
context,
).colorScheme.primaryContainer,
icon: Icons.check,
label: 'Select',
)
else
SlidableAction(
onPressed: (context) {
ref
.read(selectedContainerProvider.notifier)
.clearContainer();
},
foregroundColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
backgroundColor: Theme.of(
context,
).colorScheme.primaryContainer,
icon: Icons.close,
label: 'Unselect',
),
],
),
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: (context) {
ref
.read(selectedContainerProvider.notifier)
.clearContainer();
onPressed: (context) async {
await ref
.read(containerRepositoryProvider.notifier)
.deleteContainer(container.id);
},
foregroundColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
backgroundColor: Theme.of(
context,
).colorScheme.primaryContainer,
icon: Icons.close,
label: 'Unselect',
).colorScheme.errorContainer,
foregroundColor: Theme.of(
context,
).colorScheme.onErrorContainer,
icon: Icons.delete,
label: 'Delete',
),
],
),
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: (context) async {
await ref
.read(containerRepositoryProvider.notifier)
.deleteContainer(container.id);
},
backgroundColor: Theme.of(
context,
).colorScheme.errorContainer,
foregroundColor: Theme.of(
context,
).colorScheme.onErrorContainer,
icon: Icons.delete,
label: 'Delete',
),
],
),
child: ContainerListTile(
container,
isSelected: container.id == selectedContainer,
onTap: () async {
await ContainerEditRoute(
containerData: jsonEncode(container.toJson()),
).push(context);
},
),
);
},
);
},
),
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load containers',
exception: error,
onRetry: () => ref.invalidate(watchContainersWithCountProvider),
],
),
child: ContainerListTile(
container,
isSelected: container.id == selectedContainer,
onTap: () async {
await ContainerEditRoute(
containerData: jsonEncode(container.toJson()),
).push(context);
},
),
);
},
);
},
),
),
loading: () => ListView.builder(
itemCount: 3,
itemBuilder: (context, index) => ContainerListTile(
ContainerData(id: 'null', color: Colors.transparent),
onTap: null,
isSelected: false,
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load containers',
exception: error,
onRetry: () => ref.invalidate(watchContainersWithCountProvider),
),
),
loading: () => ListView.builder(
itemCount: 3,
itemBuilder: (context, index) => ContainerListTile(
ContainerData(id: 'null', color: Colors.transparent),
onTap: null,
isSelected: false,
),
),
),
),
@@ -45,70 +45,75 @@ class ContainerSelectionScreen extends HookConsumerWidget {
return Scaffold(
appBar: AppBar(title: const Text('Select Container')),
body: Skeletonizer(
enabled: containersAsync.isLoading,
child: containersAsync.when(
skipLoadingOnReload: true,
data: (containers) => FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return ListView.builder(
controller: controller,
itemCount: containers.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return ListTileTheme(
selectedColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
selectedTileColor: Theme.of(
context,
).colorScheme.primaryContainer,
child: ListTile(
selected: selectedContainerId == null,
leading: CircleAvatar(
backgroundColor: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
child: const Icon(MdiIcons.folderHidden),
body: SafeArea(
child: Skeletonizer(
enabled: containersAsync.isLoading,
child: containersAsync.when(
skipLoadingOnReload: true,
data: (containers) => FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return ListView.builder(
controller: controller,
itemCount: containers.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return ListTileTheme(
selectedColor: Theme.of(
context,
).colorScheme.onPrimaryContainer,
selectedTileColor: Theme.of(
context,
).colorScheme.primaryContainer,
child: ListTile(
selected: selectedContainerId == null,
leading: CircleAvatar(
backgroundColor: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
child: const Icon(MdiIcons.folderHidden),
),
title: const Text('Unassigned'),
onTap: () {
context.pop<ContainerSelectionResult>(
const ContainerSelectionResult.unassigned(),
);
},
),
title: const Text('Unassigned'),
onTap: () {
context.pop<ContainerSelectionResult>(
const ContainerSelectionResult.unassigned(),
);
},
),
);
}
final container = containers[index - 1];
return ContainerListTile(
container,
isSelected: container.id == selectedContainerId,
onTap: () {
context.pop<ContainerSelectionResult>(
ContainerSelectionResult.selected(container.id),
);
},
);
},
);
},
),
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load containers',
exception: error,
onRetry: () => ref.invalidate(watchContainersWithCountProvider),
}
final container = containers[index - 1];
return ContainerListTile(
container,
isSelected: container.id == selectedContainerId,
onTap: () {
context.pop<ContainerSelectionResult>(
ContainerSelectionResult.selected(container.id),
);
},
);
},
);
},
),
),
loading: () => ListView.builder(
itemCount: 3,
itemBuilder: (context, index) => ContainerListTile(
ContainerData(id: Namespace.nil.value, color: Colors.transparent),
isSelected: false,
onTap: null,
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load containers',
exception: error,
onRetry: () => ref.invalidate(watchContainersWithCountProvider),
),
),
loading: () => ListView.builder(
itemCount: 3,
itemBuilder: (context, index) => ContainerListTile(
ContainerData(
id: Namespace.nil.value,
color: Colors.transparent,
),
isSelected: false,
onTap: null,
),
),
),
),