Major additions since the initial rebuild: - New tabs: Trash, Shares, Recent, configurable tab visibility/order/default - Nextcloud sharing: search users/teams, internal link, email shares, public links, "others with access" - in its own Share bottom sheet - Details bottom sheet (Info/Versions/Activity) with a persistent peek panel in the media viewer, reachable from selection, swipe, and the media viewer - Files/Photos: tap-and-hold multi-select with a sticky selection toolbar, swipe actions (favorite/delete/share), sticky sort/filter controls, list/grid views, breadcrumb chips, whole-account Photos view - Media viewer: pinch/double-tap zoom on images, predictive-back support, fixed PDF zoom-out floor - Settings: Material You theming incl. AMOLED black, configurable bottom bar tabs, swipe actions, media seek bar style, and folder-listing cache policy (never/periodic/manual) to cut down on refetching - Login flow moved to an in-app Chrome Custom Tab with retry-tolerant polling - Replaced ad hoc widgets with native Material 3 equivalents (toggle IconButtons, SliverAppBar selection toolbar, SearchBar, drag handles) - Rebrand: app renamed to Noo throughout, package id -> dev.ayushya.noo, new adaptive launcher icon Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
69 lines
2.2 KiB
Dart
69 lines
2.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// A single icon button that fills with a solid circle when active —
|
|
/// the building block for Drive-style toggle controls. Thin wrapper around
|
|
/// the native Material 3 toggle `IconButton` (its `isSelected` constructor
|
|
/// param, not a hand-rolled Material+InkWell) so every call site here gets
|
|
/// the built-in selected/unselected treatment for free.
|
|
class ToggleIconButton extends StatelessWidget {
|
|
final IconData icon;
|
|
final bool isSelected;
|
|
final VoidCallback onTap;
|
|
final String? tooltip;
|
|
|
|
const ToggleIconButton({
|
|
super.key,
|
|
required this.icon,
|
|
required this.isSelected,
|
|
required this.onTap,
|
|
this.tooltip,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return IconButton(
|
|
isSelected: isSelected,
|
|
icon: Icon(icon, size: 20),
|
|
tooltip: tooltip,
|
|
onPressed: onTap,
|
|
visualDensity: VisualDensity.compact,
|
|
style: ButtonStyle(
|
|
shape: const WidgetStatePropertyAll(CircleBorder()),
|
|
foregroundColor: WidgetStateProperty.resolveWith(
|
|
(states) => states.contains(WidgetState.selected)
|
|
? colorScheme.onPrimaryContainer
|
|
: colorScheme.onSurfaceVariant,
|
|
),
|
|
backgroundColor: WidgetStateProperty.resolveWith(
|
|
(states) => states.contains(WidgetState.selected)
|
|
? colorScheme.primaryContainer
|
|
: Colors.transparent,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// A pill-shaped row of [ToggleIconButton]s sharing one background, matching
|
|
/// the list/grid view switch seen in Google Drive's toolbar.
|
|
class SegmentedIconGroup extends StatelessWidget {
|
|
final List<Widget> children;
|
|
|
|
const SegmentedIconGroup({super.key, required this.children});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
return Container(
|
|
padding: const EdgeInsets.all(2),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surfaceContainerHigh,
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: children),
|
|
);
|
|
}
|
|
}
|