// Copyright 2014 The Flutter Authors. 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';

/// Flutter code sample for [ColorScheme.fromImageProvider] with content-based dynamic color.

const Widget divider = SizedBox(height: 10);
const double narrowScreenWidthThreshold = 400;

void main() => runApp(const DynamicColorExample());

class DynamicColorExample extends StatefulWidget {
  const DynamicColorExample({this.loadColorScheme, super.key});

  static const List<ImageProvider> images = <NetworkImage>[
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_1.png',
    ),
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_2.png',
    ),
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_3.png',
    ),
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_4.png',
    ),
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_5.png',
    ),
    NetworkImage(
      'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_6.png',
    ),
  ];

  final Future<ColorScheme> Function(
    ImageProvider<Object> provider,
    Brightness brightness,
  )?
  loadColorScheme;

  @override
  State<DynamicColorExample> createState() => _DynamicColorExampleState();
}

class _DynamicColorExampleState extends State<DynamicColorExample> {
  late ColorScheme currentColorScheme;
  String currentHyperlinkImage = '';
  late int selectedImage;
  late bool isLight;
  late bool isLoading;

  @override
  void initState() {
    super.initState();
    selectedImage = 0;
    isLight = true;
    isLoading = true;
    currentColorScheme = const ColorScheme.light();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateImage(DynamicColorExample.images[selectedImage]);
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    final ColorScheme colorScheme = currentColorScheme;
    final Color selectedColor = currentColorScheme.primary;

    final ThemeData lightTheme = ThemeData(
      colorSchemeSeed: selectedColor,
      brightness: Brightness.light,
      useMaterial3: false,
    );
    final ThemeData darkTheme = ThemeData(
      colorSchemeSeed: selectedColor,
      brightness: Brightness.dark,
      useMaterial3: false,
    );

    Widget schemeLabel(String brightness, ColorScheme colorScheme) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 15),
        child: Text(
          brightness,
          style: TextStyle(
            fontWeight: FontWeight.bold,
            color: colorScheme.onSecondaryContainer,
          ),
        ),
      );
    }

    Widget schemeView(ThemeData theme) {
      return Padding(
        padding: const EdgeInsets.symmetric(horizontal: 15),
        child: ColorSchemeView(colorScheme: theme.colorScheme),
      );
    }

    return MaterialApp(
      theme: ThemeData(colorScheme: colorScheme),
      debugShowCheckedModeBanner: false,
      home: Builder(
        builder: (BuildContext context) => Scaffold(
          appBar: AppBar(
            title: const Text('Content Based Dynamic Color'),
            backgroundColor: colorScheme.primary,
            foregroundColor: colorScheme.onPrimary,
            actions: <Widget>[
              const Icon(Icons.light_mode),
              Switch(
                activeThumbColor: colorScheme.primary,
                activeTrackColor: colorScheme.surface,
                inactiveTrackColor: colorScheme.onSecondary,
                value: isLight,
                onChanged: (bool value) {
                  setState(() {
                    isLight = value;
                    _updateImage(DynamicColorExample.images[selectedImage]);
                  });
                },
              ),
            ],
          ),
          body: Center(
            child: isLoading
                ? const CircularProgressIndicator()
                : ColoredBox(
                    color: colorScheme.secondaryContainer,
                    child: Column(
                      children: <Widget>[
                        divider,
                        _imagesRow(
                          context,
                          DynamicColorExample.images,
                          colorScheme,
                        ),
                        divider,
                        Expanded(
                          child: ColoredBox(
                            color: colorScheme.surface,
                            child: LayoutBuilder(
                              builder:
                                  (
                                    BuildContext context,
                                    BoxConstraints constraints,
                                  ) {
                                    if (constraints.maxWidth <
                                        narrowScreenWidthThreshold) {
                                      return SingleChildScrollView(
                                        child: Column(
                                          children: <Widget>[
                                            divider,
                                            schemeLabel(
                                              'Light ColorScheme',
                                              colorScheme,
                                            ),
                                            schemeView(lightTheme),
                                            divider,
                                            divider,
                                            schemeLabel(
                                              'Dark ColorScheme',
                                              colorScheme,
                                            ),
                                            schemeView(darkTheme),
                                          ],
                                        ),
                                      );
                                    } else {
                                      return SingleChildScrollView(
                                        child: Padding(
                                          padding: const EdgeInsets.only(
                                            top: 5,
                                          ),
                                          child: Column(
                                            children: <Widget>[
                                              Row(
                                                children: <Widget>[
                                                  Expanded(
                                                    child: Column(
                                                      children: <Widget>[
                                                        schemeLabel(
                                                          'Light ColorScheme',
                                                          colorScheme,
                                                        ),
                                                        schemeView(lightTheme),
                                                      ],
                                                    ),
                                                  ),
                                                  Expanded(
                                                    child: Column(
                                                      children: <Widget>[
                                                        schemeLabel(
                                                          'Dark ColorScheme',
                                                          colorScheme,
                                                        ),
                                                        schemeView(darkTheme),
                                                      ],
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            ],
                                          ),
                                        ),
                                      );
                                    }
                                  },
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
          ),
        ),
      ),
    );
  }

  Future<void> _updateImage(ImageProvider provider) async {
    final ColorScheme newColorScheme;
    if (widget.loadColorScheme != null) {
      newColorScheme = await widget.loadColorScheme!(
        provider,
        isLight ? Brightness.light : Brightness.dark,
      );
    } else {
      newColorScheme = await ColorScheme.fromImageProvider(
        provider: provider,
        brightness: isLight ? Brightness.light : Brightness.dark,
      );
    }
    if (!mounted) {
      return;
    }
    setState(() {
      selectedImage = DynamicColorExample.images.indexOf(provider);
      currentColorScheme = newColorScheme;
    });
  }

  // For small screens, have two rows of image selection. For wide screens,
  // fit them onto one row.
  Widget _imagesRow(
    BuildContext context,
    List<ImageProvider> images,
    ColorScheme colorScheme,
  ) {
    final double windowHeight = MediaQuery.of(context).size.height;
    final double windowWidth = MediaQuery.of(context).size.width;
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          if (constraints.maxWidth > 800) {
            return _adaptiveLayoutImagesRow(images, colorScheme, windowHeight);
          } else {
            return Column(
              children: <Widget>[
                _adaptiveLayoutImagesRow(
                  images.sublist(0, 3),
                  colorScheme,
                  windowWidth,
                ),
                _adaptiveLayoutImagesRow(
                  images.sublist(3),
                  colorScheme,
                  windowWidth,
                ),
              ],
            );
          }
        },
      ),
    );
  }

  Widget _adaptiveLayoutImagesRow(
    List<ImageProvider> images,
    ColorScheme colorScheme,
    double windowWidth,
  ) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: images
          .map(
            (ImageProvider image) => Flexible(
              flex: (images.length / 3).floor(),
              child: GestureDetector(
                onTap: () => _updateImage(image),
                child: Card(
                  color:
                      DynamicColorExample.images.indexOf(image) == selectedImage
                      ? colorScheme.primaryContainer
                      : colorScheme.surface,
                  child: Padding(
                    padding: const EdgeInsets.all(5.0),
                    child: ConstrainedBox(
                      constraints: BoxConstraints(maxWidth: windowWidth * .25),
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(8.0),
                        child: Image(image: image),
                      ),
                    ),
                  ),
                ),
              ),
            ),
          )
          .toList(),
    );
  }
}

class ColorSchemeView extends StatelessWidget {
  const ColorSchemeView({super.key, required this.colorScheme});

  final ColorScheme colorScheme;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ColorGroup(
          children: <ColorChip>[
            ColorChip(
              label: 'primary',
              color: colorScheme.primary,
              onColor: colorScheme.onPrimary,
            ),
            ColorChip(
              label: 'onPrimary',
              color: colorScheme.onPrimary,
              onColor: colorScheme.primary,
            ),
            ColorChip(
              label: 'primaryContainer',
              color: colorScheme.primaryContainer,
              onColor: colorScheme.onPrimaryContainer,
            ),
            ColorChip(
              label: 'onPrimaryContainer',
              color: colorScheme.onPrimaryContainer,
              onColor: colorScheme.primaryContainer,
            ),
          ],
        ),
        divider,
        ColorGroup(
          children: <ColorChip>[
            ColorChip(
              label: 'secondary',
              color: colorScheme.secondary,
              onColor: colorScheme.onSecondary,
            ),
            ColorChip(
              label: 'onSecondary',
              color: colorScheme.onSecondary,
              onColor: colorScheme.secondary,
            ),
            ColorChip(
              label: 'secondaryContainer',
              color: colorScheme.secondaryContainer,
              onColor: colorScheme.onSecondaryContainer,
            ),
            ColorChip(
              label: 'onSecondaryContainer',
              color: colorScheme.onSecondaryContainer,
              onColor: colorScheme.secondaryContainer,
            ),
          ],
        ),
        divider,
        ColorGroup(
          children: <ColorChip>[
            ColorChip(
              label: 'tertiary',
              color: colorScheme.tertiary,
              onColor: colorScheme.onTertiary,
            ),
            ColorChip(
              label: 'onTertiary',
              color: colorScheme.onTertiary,
              onColor: colorScheme.tertiary,
            ),
            ColorChip(
              label: 'tertiaryContainer',
              color: colorScheme.tertiaryContainer,
              onColor: colorScheme.onTertiaryContainer,
            ),
            ColorChip(
              label: 'onTertiaryContainer',
              color: colorScheme.onTertiaryContainer,
              onColor: colorScheme.tertiaryContainer,
            ),
          ],
        ),
        divider,
        ColorGroup(
          children: <ColorChip>[
            ColorChip(
              label: 'error',
              color: colorScheme.error,
              onColor: colorScheme.onError,
            ),
            ColorChip(
              label: 'onError',
              color: colorScheme.onError,
              onColor: colorScheme.error,
            ),
            ColorChip(
              label: 'errorContainer',
              color: colorScheme.errorContainer,
              onColor: colorScheme.onErrorContainer,
            ),
            ColorChip(
              label: 'onErrorContainer',
              color: colorScheme.onErrorContainer,
              onColor: colorScheme.errorContainer,
            ),
          ],
        ),
        divider,
        ColorGroup(
          children: <ColorChip>[
            ColorChip(
              label: 'surface',
              color: colorScheme.surface,
              onColor: colorScheme.onSurface,
            ),
            ColorChip(
              label: 'onSurface',
              color: colorScheme.onSurface,
              onColor: colorScheme.surface,
            ),
            ColorChip(
              label: 'onSurfaceVariant',
              color: colorScheme.onSurfaceVariant,
              onColor: colorScheme.surfaceContainerHighest,
            ),
          ],
        ),
        divider,
        ColorGroup(
          children: <ColorChip>[
            ColorChip(label: 'outline', color: colorScheme.outline),
            ColorChip(label: 'shadow', color: colorScheme.shadow),
            ColorChip(
              label: 'inverseSurface',
              color: colorScheme.inverseSurface,
              onColor: colorScheme.onInverseSurface,
            ),
            ColorChip(
              label: 'onInverseSurface',
              color: colorScheme.onInverseSurface,
              onColor: colorScheme.inverseSurface,
            ),
            ColorChip(
              label: 'inversePrimary',
              color: colorScheme.inversePrimary,
              onColor: colorScheme.primary,
            ),
          ],
        ),
      ],
    );
  }
}

class ColorGroup extends StatelessWidget {
  const ColorGroup({super.key, required this.children});

  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Card(
        clipBehavior: Clip.antiAlias,
        child: Column(children: children),
      ),
    );
  }
}

class ColorChip extends StatelessWidget {
  const ColorChip({
    super.key,
    required this.color,
    required this.label,
    this.onColor,
  });

  final Color color;
  final Color? onColor;
  final String label;

  static Color contrastColor(Color color) {
    final Brightness brightness = ThemeData.estimateBrightnessForColor(color);
    return switch (brightness) {
      Brightness.dark => Colors.white,
      Brightness.light => Colors.black,
    };
  }

  @override
  Widget build(BuildContext context) {
    final Color labelColor = onColor ?? contrastColor(color);
    return ColoredBox(
      color: color,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: <Expanded>[
            Expanded(
              child: Text(label, style: TextStyle(color: labelColor)),
            ),
          ],
        ),
      ),
    );
  }
}
