// Copyright 2021 The Flutter team. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show SystemUiOverlayStyle; import 'package:linting_tool/layout/adaptive.dart'; import 'package:linting_tool/pages/default_lints_page.dart'; import 'package:linting_tool/pages/home_page.dart'; import 'package:linting_tool/pages/saved_lints_page.dart'; import 'package:linting_tool/theme/colors.dart'; final navKey = GlobalKey(); class AdaptiveNav extends StatefulWidget { const AdaptiveNav({super.key}); @override State createState() => _AdaptiveNavState(); } class _AdaptiveNavState extends State { @override Widget build(BuildContext context) { final isDesktop = isDisplayLarge(context); const navigationDestinations = <_Destination>[ _Destination( textLabel: 'Home', icon: Icons.home_outlined, selectedIcon: Icons.home, destination: HomePage(), ), _Destination( textLabel: 'Saved Profiles', icon: Icons.save_outlined, selectedIcon: Icons.save, destination: SavedLintsPage(), ), _Destination( textLabel: 'Default Profiles', icon: Icons.featured_play_list_outlined, selectedIcon: Icons.featured_play_list, destination: DefaultLintsPage(), ), ]; final trailing = { 'About': Icons.info_outline, }; return _NavView( extended: isDesktop, destinations: navigationDestinations, trailing: trailing, ); } } class _NavView extends StatefulWidget { const _NavView({ required this.extended, required this.destinations, this.trailing, }); final bool extended; final List<_Destination> destinations; final Map? trailing; @override _NavViewState createState() => _NavViewState(); } class _NavViewState extends State<_NavView> { late ValueNotifier _isExtended; var _selectedIndex = 0; @override void initState() { super.initState(); _isExtended = ValueNotifier(widget.extended); } void _onDestinationSelected(int index) { setState(() { _selectedIndex = index; }); } @override Widget build(BuildContext context) { var textTheme = Theme.of(context).textTheme; return Scaffold( appBar: AppBar( title: Text( 'Flutter Linting Tool', style: textTheme.subtitle2!.copyWith( color: textTheme.bodyText1!.color, ), ), toolbarHeight: 38.0, backgroundColor: Colors.white, systemOverlayStyle: SystemUiOverlayStyle.dark, ), body: Row( children: [ LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( clipBehavior: Clip.antiAlias, child: ConstrainedBox( constraints: BoxConstraints( minHeight: constraints.maxHeight, ), child: IntrinsicHeight( child: ValueListenableBuilder( valueListenable: _isExtended, builder: (context, value, child) { var isSmallDisplay = isDisplaySmall(context); return NavigationRail( destinations: [ for (var destination in widget.destinations) NavigationRailDestination( icon: Icon(destination.icon), selectedIcon: destination.selectedIcon != null ? Icon(destination.selectedIcon) : null, label: Text(destination.textLabel), ), ], extended: _isExtended.value! && !isSmallDisplay, labelType: NavigationRailLabelType.none, leading: _NavigationRailHeader( extended: _isExtended, ), trailing: _NavigationRailTrailingSection( trailingDestinations: widget.trailing!, ), selectedIndex: _selectedIndex, onDestinationSelected: _onDestinationSelected, ); }, ), ), ), ); }, ), const VerticalDivider(thickness: 1, width: 1), Expanded( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1340), child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), switchOutCurve: Curves.easeOut, switchInCurve: Curves.easeIn, child: widget.destinations[_selectedIndex].destination, ), ), ), ), ], ), ); } } class _NavigationRailHeader extends StatelessWidget { const _NavigationRailHeader({ required this.extended, }); final ValueNotifier extended; @override Widget build(BuildContext context) { final textTheme = Theme.of(context).textTheme; final animation = NavigationRail.extendedAnimation(context); return AnimatedBuilder( animation: animation, builder: (context, child) { return Align( alignment: AlignmentDirectional.centerStart, widthFactor: animation.value, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 56, child: Row( children: [ const SizedBox(width: 6), InkWell( borderRadius: const BorderRadius.all(Radius.circular(16)), onTap: () { extended.value = !extended.value!; }, child: Row( children: [ Transform.rotate( angle: animation.value * math.pi, child: const Icon( Icons.arrow_left, color: AppColors.white50, size: 16, ), ), const FlutterLogo(), const SizedBox(width: 10), Align( alignment: AlignmentDirectional.centerStart, widthFactor: animation.value, child: Opacity( opacity: animation.value, child: Text( 'Linting Tool', style: textTheme.bodyText1!.copyWith( color: AppColors.white50, ), ), ), ), SizedBox(width: 18 * animation.value), ], ), ), ], ), ), const SizedBox(height: 8), ], ), ); }, ); } } class _NavigationRailTrailingSection extends StatelessWidget { const _NavigationRailTrailingSection({ required this.trailingDestinations, }); final Map trailingDestinations; @override Widget build(BuildContext context) { final theme = Theme.of(context); final textTheme = theme.textTheme; final navigationRailTheme = theme.navigationRailTheme; final animation = NavigationRail.extendedAnimation(context); return AnimatedBuilder( animation: animation, builder: (context, child) { return Visibility( maintainAnimation: true, maintainState: true, visible: animation.value > 0, child: Opacity( opacity: animation.value, child: Align( widthFactor: animation.value, alignment: AlignmentDirectional.centerStart, child: SizedBox( height: 485, width: 256, child: ListView( padding: const EdgeInsets.all(12), physics: const NeverScrollableScrollPhysics(), children: [ const Divider( color: AppColors.blue200, thickness: 0.4, indent: 14, endIndent: 16, ), const SizedBox(height: 8), for (var item in trailingDestinations.keys) InkWell( borderRadius: const BorderRadius.all( Radius.circular(36), ), onTap: () => _onTapped(context, item), child: Column( children: [ Row( children: [ const SizedBox(width: 12), Icon( trailingDestinations[item], color: AppColors.blue300, ), const SizedBox(width: 24), Text( item, style: textTheme.bodyText1!.copyWith( color: navigationRailTheme .unselectedLabelTextStyle!.color, ), ), const SizedBox(height: 72), ], ), ], ), ), ], ), ), ), ), ); }, ); } void _onTapped(BuildContext context, String key) { switch (key) { case 'About': showAboutDialog( context: context, applicationIcon: const FlutterLogo(), children: [ const Text( 'A tool that helps you manage linter rules for your Flutter projects.', ), ], ); break; default: break; } } } class _Destination { const _Destination({ required this.destination, required this.textLabel, required this.icon, this.selectedIcon, }); final String textLabel; final IconData icon; final IconData? selectedIcon; final Widget destination; }