content: // 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 'package:flutter/material.dart';
  
  import '../../../../../shared/const/app.dart';
  import '../../../../../shared/controllers/theme_controller.dart';
  import '../../../../../shared/utils/link_text_span.dart';
  import 'color_palettes_screen.dart';
  import 'component_screen.dart';
  import 'constants.dart';
  import 'elevation_screen.dart';
  import 'typography_screen.dart';
  
  // TODO(rydmike): Investigate why we in this Flutter version suddenly need to
  //  ignore discarded futures for AnimationController()..forward
  // ignore_for_file: discarded_futures
  
  class AppExampleMaterial3 extends StatefulWidget {
    const AppExampleMaterial3({super.key, required this.themeController});
    final ThemeController themeController;
  
    @override
    State<AppExampleMaterial3> createState() => _AppExampleMaterial3State();
  }
  
  class _AppExampleMaterial3State extends State<AppExampleMaterial3>
      with TickerProviderStateMixin {
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
    late final AnimationController oneTwoController;
    late final CurvedAnimation oneTwoAnimation;
    late final AnimationController railController;
    late final CurvedAnimation railAnimation;
    bool oneTwoControllerInitialized = false;
    bool railControllerInitialized = false;
    bool showMediumSizeLayout = false;
    bool showLargeSizeLayout = false;
    bool showRail = false;
  
    int screenIndex = ScreenSelected.component.value;
  
    @override
    void initState() {
      super.initState();
      oneTwoController = AnimationController(
        duration: Duration(milliseconds: kTransitionLength.toInt() * 2),
        value: 0,
        vsync: this,
      );
      oneTwoAnimation = CurvedAnimation(
        parent: oneTwoController,
        curve: const Interval(0.5, 1.0),
      );
      railController = AnimationController(
        duration: Duration(milliseconds: kTransitionLength.toInt() * 2),
        value: 0,
        vsync: this,
      );
      railAnimation = CurvedAnimation(
        parent: railController,
        curve: const Interval(0.5, 1.0),
      );
    }
  
    @override
    void dispose() {
      oneTwoController.dispose();
      super.dispose();
    }
  
    @override
    void didChangeDependencies() {
      super.didChangeDependencies();
  
      final double width = MediaQuery.sizeOf(context).width;
      final AnimationStatus oneTwoStatus = oneTwoController.status;
      final AnimationStatus railStatus = railController.status;
      // Use rail or navigation bar
      if (width > kNarrowScreenWidthThreshold) {
        showRail = true;
        showMediumSizeLayout = false;
        showLargeSizeLayout = false;
        if (railStatus != AnimationStatus.forward &&
            railStatus != AnimationStatus.completed) {
          railController.forward();
        }
      } else {
        showRail = false;
        showMediumSizeLayout = false;
        showLargeSizeLayout = false;
        if (railStatus != AnimationStatus.reverse &&
            railStatus != AnimationStatus.dismissed) {
          railController.reverse();
        }
      }
      if (!railControllerInitialized) {
        railControllerInitialized = true;
        railController.value = width > kNarrowScreenWidthThreshold ? 1 : 0;
      }
      // One or Two colum with rail
      if (width > kMediumWidthBreakpoint) {
        if (width > kLargeWidthBreakpoint) {
          showMediumSizeLayout = false;
          showLargeSizeLayout = true;
        } else {
          showMediumSizeLayout = true;
          showLargeSizeLayout = false;
        }
        if (oneTwoStatus != AnimationStatus.forward &&
            oneTwoStatus != AnimationStatus.completed) {
          oneTwoController.forward();
        }
      } else {
        showMediumSizeLayout = false;
        showLargeSizeLayout = false;
        if (oneTwoStatus != AnimationStatus.reverse &&
            oneTwoStatus != AnimationStatus.dismissed) {
          oneTwoController.reverse();
        }
      }
      if (!oneTwoControllerInitialized) {
        oneTwoControllerInitialized = true;
        oneTwoController.value = width > kMediumWidthBreakpoint ? 1 : 0;
      }
      setState(() {});
    }
  
    void handleScreenChanged(int screenSelected) {
      setState(() {
        screenIndex = screenSelected;
      });
    }
  
    Widget createScreenFor(
        ScreenSelected screenSelected, bool showNavBarExample) {
      switch (screenSelected) {
        case ScreenSelected.component:
          return Expanded(
            child: OneTwoTransition(
              animation: oneTwoAnimation,
              one: FirstComponentList(
                  showNavBottomBar: showNavBarExample,
                  scaffoldKey: scaffoldKey,
                  showSecondList: showMediumSizeLayout || showLargeSizeLayout),
              two: SecondComponentList(
                  scaffoldKey: scaffoldKey,
                  showSecondList: showMediumSizeLayout || showLargeSizeLayout),
            ),
          );
        case ScreenSelected.color:
          return ColorPalettesScreen(themeController: widget.themeController);
        case ScreenSelected.typography:
          return const TypographyScreen();
        case ScreenSelected.elevation:
          return const ElevationScreen();
      }
    }
  
    PreferredSizeWidget createAppBar() {
      return AppBar(
        title: Theme.of(context).useMaterial3
            ? const Text('Material 3')
            : const Text('Material 2'),
        leading: const Material3About(useRootNavigator: false),
        actions: !showRail
            ? <Widget>[
                _BrightnessButton(controller: widget.themeController),
                _Material3Button(controller: widget.themeController),
              ]
            : <Widget>[const SizedBox.shrink()],
      );
    }
  
    Widget _expandedTrailingActions() => Container(
          constraints: const BoxConstraints.tightFor(width: kRailExpandedWidth),
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Row(
                children: <Widget>[
                  const Text('Light'),
                  const Spacer(),
                  Switch(
                      value: Theme.of(context).brightness == Brightness.light,
                      onChanged: (bool value) {
                        if (value) {
                          widget.themeController.setThemeMode(ThemeMode.light);
                        } else {
                          widget.themeController.setThemeMode(ThemeMode.dark);
                        }
                      })
                ],
              ),
              Row(
                children: <Widget>[
                  const Text('Material 3'),
                  const Spacer(),
                  Switch(
                    value: widget.themeController.useMaterial3,
                    onChanged: widget.themeController.setUseMaterial3,
                  )
                ],
              ),
            ],
          ),
        );
  
    Widget _trailingActions() => Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Flexible(
              child: _BrightnessButton(
                controller: widget.themeController,
                showTooltipBelow: false,
              ),
            ),
            Flexible(
              child: _Material3Button(
                controller: widget.themeController,
                showTooltipBelow: false,
              ),
            ),
          ],
        );
  
    @override
    Widget build(BuildContext context) {
      return AnimatedBuilder(
        animation: oneTwoController,
        builder: (BuildContext context, Widget? child) {
          return AnimatedBuilder(
              animation: railAnimation,
              builder: (BuildContext context, Widget? child) {
                return NavigationTransition(
                  scaffoldKey: scaffoldKey,
                  animationController: railController,
                  railAnimation: railAnimation,
                  appBar: createAppBar(),
                  body: createScreenFor(ScreenSelected.values[screenIndex],
                      railController.value == 1),
                  navigationRail: NavigationRail(
                    minExtendedWidth: kRailExpandedWidth,
                    extended: showLargeSizeLayout,
                    // The Rail is silly, it does not expand if type is something
                    // else than none, e.g. set via themes.
                    labelType:
                        showLargeSizeLayout ? NavigationRailLabelType.none : null,
                    destinations: navRailDestinations,
                    selectedIndex: screenIndex,
                    onDestinationSelected: (int index) {
                      setState(() {
                        screenIndex = index;
                        handleScreenChanged(screenIndex);
                      });
                    },
                    trailing: Expanded(
                      child: Padding(
                        padding: const EdgeInsets.only(bottom: 20),
                        child: showLargeSizeLayout
                            ? _expandedTrailingActions()
                            : _trailingActions(),
                      ),
                    ),
                  ),
                  navigationBar: NavigationBars(
                    onSelectItem: (int index) {
                      setState(() {
                        screenIndex = index;
                        handleScreenChanged(screenIndex);
                      });
                    },
                    selectedIndex: screenIndex,
                    isExampleBar: false,
                  ),
                );
              });
        },
      );
    }
  }
  
  class _BrightnessButton extends StatelessWidget {
    const _BrightnessButton({
      this.showTooltipBelow = true,
      required this.controller,
    });
    final bool showTooltipBelow;
    final ThemeController controller;
  
    @override
    Widget build(BuildContext context) {
      final bool isBright = Theme.of(context).brightness == Brightness.light;
      return Tooltip(
        preferBelow: showTooltipBelow,
        message: isBright ? 'Set to dark' : 'Set to light',
        child: IconButton(
          icon: isBright
              ? const Icon(Icons.dark_mode_outlined)
              : const Icon(Icons.light_mode_outlined),
          onPressed: () {
            if (isBright) {
              controller.setThemeMode(ThemeMode.dark);
            } else {
              controller.setThemeMode(ThemeMode.light);
            }
          },
        ),
      );
    }
  }
  
  class _Material3Button extends StatelessWidget {
    const _Material3Button({
      this.showTooltipBelow = true,
      required this.controller,
    });
    final bool showTooltipBelow;
    final ThemeController controller;
  
    @override
    Widget build(BuildContext context) {
      final bool useMaterial3 = Theme.of(context).useMaterial3;
      return Tooltip(
        preferBelow: showTooltipBelow,
        message: 'Switch to Material ${useMaterial3 ? 2 : 3}',
        child: IconButton(
            icon: useMaterial3
                ? const Icon(Icons.looks_two_outlined)
                : const Icon(Icons.looks_3_outlined),
            onPressed: () {
              controller.setUseMaterial3(!controller.useMaterial3);
            }),
      );
    }
  }
  
  class NavigationTransition extends StatefulWidget {
    const NavigationTransition(
        {super.key,
        required this.scaffoldKey,
        required this.animationController,
        required this.railAnimation,
        required this.navigationRail,
        required this.navigationBar,
        required this.appBar,
        required this.body});
  
    final GlobalKey<ScaffoldState> scaffoldKey;
    final AnimationController animationController;
    final CurvedAnimation railAnimation;
    final Widget navigationRail;
    final Widget navigationBar;
    final PreferredSizeWidget appBar;
    final Widget body;
  
    @override
    State<NavigationTransition> createState() => _NavigationTransitionState();
  }
  
  class _NavigationTransitionState extends State<NavigationTransition> {
    late final AnimationController controller;
    late final CurvedAnimation railAnimation;
    late final ReverseAnimation barAnimation;
    bool controllerInitialized = false;
    bool showDivider = false;
  
    @override
    void initState() {
      super.initState();
  
      controller = widget.animationController;
      railAnimation = widget.railAnimation;
  
      barAnimation = ReverseAnimation(
        CurvedAnimation(
          parent: controller,
          curve: const Interval(0.0, 0.5),
        ),
      );
    }
  
    @override
    Widget build(BuildContext context) {
      final ColorScheme colorScheme = Theme.of(context).colorScheme;
  
      return Scaffold(
        extendBodyBehindAppBar: true,
        extendBody: true,
        key: widget.scaffoldKey,
        appBar: widget.appBar,
        backgroundColor: colorScheme.surface,
        body: Row(
          children: <Widget>[
            RailTransition(
              animation: railAnimation,
              backgroundColor: colorScheme.surface,
              child: widget.navigationRail,
            ),
            widget.body,
          ],
        ),
        bottomNavigationBar: BarTransition(
          animation: barAnimation,
          backgroundColor: colorScheme.surface,
          child: widget.navigationBar,
        ),
        endDrawer: const NavigationDrawerSection(),
      );
    }
  }
  
  final List<NavigationRailDestination> navRailDestinations = appBarDestinations
      .map(
        (NavigationDestination destination) => NavigationRailDestination(
          icon: Tooltip(
            message: destination.label,
            child: destination.icon,
          ),
          selectedIcon: Tooltip(
            message: destination.label,
            child: destination.selectedIcon,
          ),
          label: Text(destination.label),
        ),
      )
      .toList();
  
  class SizeAnimation extends CurvedAnimation {
    SizeAnimation(Animation<double> parent)
        : super(
            parent: parent,
            curve: const Interval(
              0.2,
              0.8,
              curve: Curves.easeInOutCubicEmphasized,
            ),
            reverseCurve: Interval(
              0,
              0.2,
              curve: Curves.easeInOutCubicEmphasized.flipped,
            ),
          );
  }
  
  class OffsetAnimation extends CurvedAnimation {
    OffsetAnimation(Animation<double> parent)
        : super(
            parent: parent,
            curve: const Interval(
              0.4,
              1.0,
              curve: Curves.easeInOutCubicEmphasized,
            ),
            reverseCurve: Interval(
              0,
              0.2,
              curve: Curves.easeInOutCubicEmphasized.flipped,
            ),
          );
  }
  
  class RailTransition extends StatefulWidget {
    const RailTransition(
        {super.key,
        required this.animation,
        required this.backgroundColor,
        required this.child});
  
    final Animation<double> animation;
    final Widget child;
    final Color backgroundColor;
  
    @override
    State<RailTransition> createState() => _RailTransition();
  }
  
  class _RailTransition extends State<RailTransition> {
    late Animation<Offset> offsetAnimation;
    late Animation<double> widthAnimation;
  
    @override
    void didChangeDependencies() {
      super.didChangeDependencies();
  
      // The animations are only rebuilt by this method when the text
      // direction changes because this widget only depends on Directionality.
      final bool ltr = Directionality.of(context) == TextDirection.ltr;
  
      widthAnimation = Tween<double>(
        begin: 0,
        end: 1,
      ).animate(SizeAnimation(widget.animation));
  
      offsetAnimation = Tween<Offset>(
        begin: ltr ? const Offset(-1, 0) : const Offset(1, 0),
        end: Offset.zero,
      ).animate(OffsetAnimation(widget.animation));
    }
  
    @override
    Widget build(BuildContext context) {
      return ClipRect(
        child: DecoratedBox(
          decoration: BoxDecoration(color: widget.backgroundColor),
          child: Align(
            alignment: Alignment.topLeft,
            widthFactor: widthAnimation.value,
            child: FractionalTranslation(
              translation: offsetAnimation.value,
              child: widget.child,
            ),
          ),
        ),
      );
    }
  }
  
  class BarTransition extends StatefulWidget {
    const BarTransition(
        {super.key,
        required this.animation,
        required this.backgroundColor,
        required this.child});
  
    final Animation<double> animation;
    final Color backgroundColor;
    final Widget child;
  
    @override
    State<BarTransition> createState() => _BarTransition();
  }
  
  class _BarTransition extends State<BarTransition> {
    late final Animation<Offset> offsetAnimation;
    late final Animation<double> heightAnimation;
  
    @override
    void initState() {
      super.initState();
  
      offsetAnimation = Tween<Offset>(
        begin: const Offset(0, 1),
        end: Offset.zero,
      ).animate(OffsetAnimation(widget.animation));
  
      heightAnimation = Tween<double>(
        begin: 0,
        end: 1,
      ).animate(SizeAnimation(widget.animation));
    }
  
    @override
    Widget build(BuildContext context) {
      return ClipRect(
        child: Align(
          alignment: Alignment.topLeft,
          heightFactor: heightAnimation.value,
          child: FractionalTranslation(
            translation: offsetAnimation.value,
            child: widget.child,
          ),
        ),
      );
    }
  }
  
  class OneTwoTransition extends StatefulWidget {
    const OneTwoTransition({
      super.key,
      required this.animation,
      required this.one,
      required this.two,
    });
  
    final Animation<double> animation;
    final Widget one;
    final Widget two;
  
    @override
    State<OneTwoTransition> createState() => _OneTwoTransitionState();
  }
  
  class _OneTwoTransitionState extends State<OneTwoTransition> {
    late final Animation<Offset> offsetAnimation;
    late final Animation<double> widthAnimation;
  
    @override
    void initState() {
      super.initState();
  
      offsetAnimation = Tween<Offset>(
        begin: const Offset(1, 0),
        end: Offset.zero,
      ).animate(OffsetAnimation(widget.animation));
  
      widthAnimation = Tween<double>(
        begin: 0,
        end: 1000,
      ).animate(SizeAnimation(widget.animation));
    }
  
    @override
    Widget build(BuildContext context) {
      return Row(
        children: <Widget>[
          Flexible(
            flex: 1100,
            child: widget.one,
          ),
          if (widthAnimation.value.toInt() > 0) ...<Widget>[
            Flexible(
              flex: widthAnimation.value.toInt(),
              child: FractionalTranslation(
                translation: offsetAnimation.value,
                child: widget.two,
              ),
            )
          ],
        ],
      );
    }
  }
  
  /// An about icon button used on the example's app app bar.
  class Material3About extends StatelessWidget {
    const Material3About({super.key, this.useRootNavigator = true});
  
    /// Use root navigator?
    final bool useRootNavigator;
  
    @override
    Widget build(BuildContext context) {
      return IconButton(
        icon: const Icon(Icons.info),
        onPressed: () {
          showMaterial3AboutDialog(context, useRootNavigator);
        },
      );
    }
  }
  
  // This [showAppAboutDialog] function is based on the [AboutDialog] example
  // that exist(ed) in the Flutter Gallery App.
  void showMaterial3AboutDialog(BuildContext context,
      [bool useRootNavigator = true]) {
    final ThemeData theme = Theme.of(context);
    final TextStyle aboutTextStyle = theme.textTheme.bodyLarge!;
    final TextStyle footerStyle = theme.textTheme.bodySmall!;
    final TextStyle linkStyle =
        theme.textTheme.bodyLarge!.copyWith(color: theme.colorScheme.primary);
  
    final Size mediaSize = MediaQuery.sizeOf(context);
    final double width = mediaSize.width;
    final double height = mediaSize.height;
  
    final Uri sourceLink = Uri(
      scheme: 'https',
      host: 'github.com',
      path: 'flutter/samples/tree/main/material_3_demo',
    );
  
    showAboutDialog(
      context: context,
      applicationName: 'Material3 Demo',
      applicationVersion: 'Made for Flutter 3.10',
      useRootNavigator: useRootNavigator,
      applicationIcon: Icon(
        Icons.looks_3_outlined,
        size: 55,
        color: theme.colorScheme.primary,
      ),
      applicationLegalese: '© 2021-2023 The Flutter Team\nBSD-style license.',
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(top: 24),
          child: RichText(
            text: TextSpan(
              children: <TextSpan>[
                TextSpan(
                  style: aboutTextStyle,
                  text: 'This is a slightly modified version of the '
                      'official Material 3 demo app found in ',
                ),
                LinkTextSpan(
                  style: linkStyle,
                  uri: sourceLink,
                  text: 'flutter/samples',
                ),
                TextSpan(
                  style: aboutTextStyle,
                  text: '. It is included here to show the configured theme '
                      'using the official Material 3 sample app.\n\n',
                ),
                TextSpan(
                  style: footerStyle,
                  text: 'Built with Flutter ${App.flutterVersion}, '
                      'using ${App.packageName} '
                      '${App.version}\n'
                      'Media size (w:${width.toStringAsFixed(0)}, '
                      'h:${height.toStringAsFixed(0)})\n\n',
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
  