Browse Source

neon: refactor Drawer

make neon use a NavigationDrawer migrating the drawer to m3
pull/387/head
Nikolas Rimikis 1 year ago
parent
commit
8cdee9cb00
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 2
      packages/neon/neon/lib/neon.dart
  2. 150
      packages/neon/neon/lib/src/pages/home.dart
  3. 210
      packages/neon/neon/lib/src/widgets/drawer.dart
  4. 1
      packages/neon/neon/pubspec.yaml

2
packages/neon/neon/lib/neon.dart

@ -25,6 +25,8 @@ import 'package:neon/src/blocs/blocs.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/push_notification.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/widgets/drawer.dart';
import 'package:neon/src/widgets/drawer_destination.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p;

150
packages/neon/neon/lib/src/pages/home.dart

@ -189,155 +189,7 @@ class _HomePageState extends State<HomePage> {
final drawerAlwaysVisible = navigationMode == NavigationMode.drawerAlwaysVisible;
final drawer = Builder(
builder: (final context) => Drawer(
child: Column(
children: [
Expanded(
child: Scrollbar(
controller: drawerScrollController,
interactive: true,
child: ListView(
controller: drawerScrollController,
// Needed for the drawer header to also render in the statusbar
padding: EdgeInsets.zero,
children: [
Builder(
builder: (final context) {
if (accountsSnapshot.hasData) {
return DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (capabilities.data != null) ...[
if (capabilities.data!.capabilities.theming?.name != null) ...[
Text(
capabilities.data!.capabilities.theming!.name!,
style: DefaultTextStyle.of(context).style.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
],
if (capabilities.data!.capabilities.theming?.logo != null) ...[
Flexible(
child: NeonCachedUrlImage(
url: capabilities.data!.capabilities.theming!.logo!,
),
),
],
] else ...[
NeonException(
capabilities.error,
onRetry: _capabilitiesBloc.refresh,
),
NeonLinearProgressIndicator(
visible: capabilities.loading,
),
],
if (accounts.length != 1) ...[
DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
dropdownColor: Theme.of(context).colorScheme.primary,
iconEnabledColor: Theme.of(context).colorScheme.onBackground,
value: _account.id,
items: accounts
.map<DropdownMenuItem<String>>(
(final account) => DropdownMenuItem<String>(
value: account.id,
child: NeonAccountTile(
account: account,
dense: true,
textColor:
Theme.of(context).appBarTheme.foregroundColor,
),
),
)
.toList(),
onChanged: (final id) {
if (id != null) {
_accountsBloc.setActiveAccount(accounts.find(id)!);
}
},
),
),
],
],
),
);
}
return Container();
},
),
NeonException(
appImplementations.error,
onRetry: _appsBloc.refresh,
),
NeonLinearProgressIndicator(
visible: appImplementations.loading,
),
if (appImplementations.data != null) ...[
for (final appImplementation in appImplementations.data!) ...[
StreamBuilder<int>(
stream: appImplementation.getUnreadCounter(_appsBloc),
builder: (final context, final unreadCounterSnapshot) {
final unreadCount = unreadCounterSnapshot.data ?? 0;
return ListTile(
key: Key('app-${appImplementation.id}'),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(appImplementation.name(context)),
if (unreadCount > 0) ...[
Text(
unreadCount.toString(),
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
],
),
leading: appImplementation.buildIcon(),
minLeadingWidth: 0,
onTap: () async {
await _appsBloc.setActiveApp(appImplementation.id);
if (!mounted) {
return;
}
Scaffold.maybeOf(context)?.closeDrawer();
},
);
},
),
],
],
],
),
),
),
ListTile(
key: const Key('settings'),
title: Text(AppLocalizations.of(context).settings),
leading: const Icon(Icons.settings),
minLeadingWidth: 0,
onTap: () async {
Scaffold.maybeOf(context)?.closeDrawer();
const SettingsRoute().go(context);
},
),
],
),
),
);
const drawer = NeonDrawer();
final appBar = AppBar(
automaticallyImplyLeading: !drawerAlwaysVisible,
title: Column(

210
packages/neon/neon/lib/src/widgets/drawer.dart

@ -0,0 +1,210 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/neon.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/widgets/drawer_destination.dart';
import 'package:provider/provider.dart';
@internal
class NeonDrawer extends StatelessWidget {
const NeonDrawer({
super.key,
});
@override
Widget build(final BuildContext context) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final appsBloc = accountsBloc.activeAppsBloc;
return StreamBuilder(
stream: appsBloc.appImplementations,
builder: (final context, final snapshot) {
if (snapshot.data?.data == null) {
return Container();
}
return _NeonDrawer(
apps: snapshot.data!.data!,
);
},
);
}
}
class _NeonDrawer extends StatefulWidget {
const _NeonDrawer({
required this.apps,
});
final Iterable<AppImplementation> apps;
@override
State<_NeonDrawer> createState() => __NeonDrawerState();
}
class __NeonDrawerState extends State<_NeonDrawer> with SingleTickerProviderStateMixin {
late TabController _tabController;
late AccountsBloc _accountsBloc;
late AppsBloc _appsBloc;
late List<AppImplementation> _apps;
int _activeApp = 0;
@override
void initState() {
super.initState();
_accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
_appsBloc = _accountsBloc.activeAppsBloc;
_apps = widget.apps.toList();
_activeApp = _apps.indexWhere((final app) => app.id == _appsBloc.activeAppID.valueOrNull);
_tabController = TabController(
vsync: this,
length: widget.apps.length,
);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
void onAppChange(final int index) {
Scaffold.maybeOf(context)?.closeDrawer();
// selected item is not a registered app like the SettingsPage
if (index >= _apps.length) {
const SettingsRoute().go(context);
return;
}
setState(() {
_activeApp = index;
});
unawaited(_appsBloc.setActiveApp(_apps[index].id));
//context.goNamed(apps[index].routeName);
}
@override
Widget build(final BuildContext context) {
final appDestinations = _apps.map(
(final app) => NavigationDrawerDestinationExtension.fromNeonDestination(
app.destination(context),
),
);
final drawer = NavigationDrawer(
selectedIndex: _activeApp,
onDestinationSelected: onAppChange,
children: [
const NeonDrawerHeader(),
...appDestinations,
NavigationDrawerDestination(
icon: const Icon(Icons.settings),
label: Text(AppLocalizations.of(context).settings),
),
],
);
return drawer;
}
}
@internal
class NeonDrawerHeader extends StatelessWidget {
const NeonDrawerHeader({super.key});
@override
Widget build(final BuildContext context) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final capabilitiesBloc = accountsBloc.activeCapabilitiesBloc;
final accountSelecor = StreamBuilder<List<Account>>(
stream: accountsBloc.accounts,
builder: (final context, final accountsSnapshot) {
final accounts = accountsSnapshot.data;
if (accounts == null || accounts.length <= 1) {
return const SizedBox.shrink();
}
final items = accounts.map((final account) {
final child = NeonAccountTile(
account: account,
dense: true,
textColor: Theme.of(context).appBarTheme.foregroundColor,
);
return DropdownMenuItem(
value: account,
child: child,
);
}).toList();
return DropdownButtonHideUnderline(
child: DropdownButton(
isExpanded: true,
dropdownColor: Theme.of(context).colorScheme.primary,
iconEnabledColor: Theme.of(context).colorScheme.onBackground,
value: accountsBloc.activeAccount.value,
items: items,
onChanged: (final account) {
if (account == null) {
return;
}
accountsBloc.setActiveAccount(account);
},
),
);
},
);
return ResultBuilder<Capabilities>(
stream: capabilitiesBloc.capabilities,
builder: (final context, final capabilities) => DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (capabilities.data != null) ...[
if (capabilities.data!.capabilities.theming?.name != null) ...[
Text(
capabilities.data!.capabilities.theming!.name!,
style: DefaultTextStyle.of(context).style.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
],
if (capabilities.data!.capabilities.theming?.logo != null) ...[
Flexible(
child: NeonCachedUrlImage(
url: capabilities.data!.capabilities.theming!.logo!,
),
),
],
] else ...[
NeonException(
capabilities.error,
onRetry: capabilitiesBloc.refresh,
),
NeonLinearProgressIndicator(
visible: capabilities.loading,
),
],
accountSelecor,
],
),
),
);
}
}

1
packages/neon/neon/pubspec.yaml

@ -26,6 +26,7 @@ dependencies:
intl: ^0.18.0
json_annotation: ^4.8.1
material_design_icons_flutter: ^7.0.7296
meta: ^1.9.1
nextcloud:
git:
url: https://github.com/provokateurin/nextcloud-neon

Loading…
Cancel
Save