aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.32k stars 248 forks source link

Improve theming documentation for Authenticator Widget #3755

Open 94Sip opened 1 year ago

94Sip commented 1 year ago

Description

The colors that the Authenticator renders are different between iPhone and iPad, and I'm not sure exactly what to change to make them identical.

With Vue, you have provided explicit elements to alter the look and feel, but with Flutter, it seems to be solely based on the Theme. So how do I get the Authenticator to render the same regardless of iPhone or iPad?

I have a Theme defined in my project and reference that Theme as a part of instantiating my MaterialApp, and I am using Material 3:

child: MaterialApp.router(theme: MyTheme.defaultTheme, builder: Authenticator.builder(), routerDelegate: routerDelegate, routeInformationParser: BeamerParser(), backButtonDispatcher: BeamerBackButtonDispatcher(delegate: routerDelegate,),),),

Here is how it renders on iPhone:

image

Here is how it renders on iPad:

image

Categories

Steps to Reproduce

I'm simply starting a debugging session using the xcode simulator, and choosing different devices (iPhone 14 Pro, and iPad Pro 4th generation).

Screenshots

No response

Platforms

Flutter Version

3.13.4

Amplify Flutter Version

1.4..1 (Authenticator)

Deployment Method

Amplify CLI

Schema

No response

94Sip commented 1 year ago

So, after doing some digging, on the iPad, the background of the Authenticator is utilizing colorScheme.surface? But the iPhone uses something else.

So, perhaps it would be good to understand those so we can better Theme our apps?

Jordan-Nelson commented 1 year ago

Hello @94Sip - Thanks for taking the time to open the issue. The Authenticator is responsive to the screen size. On larger screens, the forms are displayed inside a Material Card. There is likely something in the theme you are using that is setting this color to green. Would you be able to share MyTheme.defaultTheme?

I think we could improve the documentation to make it more clear where each Material widget is used so that it is more obvious how to theme the authenticator. We do list the Material widgets that are used here (which seems to be missing the Card widget at the moment) but it isn't very clear where those widgets are used. Would it help to have a list of where each widget is used?

94Sip commented 1 year ago

Are you sure about the Card on the iPad? What I have found is that that "Card" on the iPad does not use cardTheme, that I have defined in my Theme, instead it is using colorScheme.surface.

As you suggested, it would definitely be easier if we could understand what Material elements are being used between the different screen sizes, because that does explain why I was seeing different behavior between the two devices. And yes, I saw the widget list, but I didn't see Card listed, and that is what was causing my confusion.

As my 2nd comment/post alluded to, I was able to dig around and find that colorScheme.surface was being employed as that green background (the Card) on the iPad, whereas on the iPad, the background color was something else entirely.

Jordan-Nelson commented 1 year ago

Are you sure about the Card on the iPad? What I have found is that that "Card" on the iPad does not use cardTheme, that I have defined in my Theme, instead it is using colorScheme.surface.

That is not consistent with that I am seeing, but if you share your theme I can attempt to reproduce. The background color of the Card is set differently in material 2 and material 3, and as often is the case with flutter theming, there might be more than one way to set it. Here is an example of how you can set it using material 2 and material 3.

// material 2
theme: ThemeData.light(useMaterial3: false).copyWith(
  cardColor: Colors.green,
),

// material 3
theme: ThemeData.light(useMaterial3: true).copyWith(
  cardTheme: const CardTheme(surfaceTintColor: Colors.red),
),
94Sip commented 1 year ago

Below is my entire theme that is now doing what I want, which I believe conflicts with what you are saying unless the Authenticator is using Material 2.

NOTE: colorScheme.surface is the color of the background that I want on the iPad, which matches the iPhone. Previously that was set to SecondaryColor (green) that I did NOT want.

I separately enabled cardTheme in MyTheme which makes the color of the cards like I want them.

So, it appears that Authenticator is using colorScheme.surface NOT the cardTheme.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:smarthunt/platform_specific.dart';

const PrimaryColor = Color(0xFF7a7a3a);
const PrimaryColorLight = Color(0xFFaaa966);
const PrimaryColorDark = Color(0xFF4c4e0f);
const SecondaryColor = Color(0xFF3b5b1d);
const SecondaryColorLight = Color(0xFF688847);
const SecondaryColorDark = Color(0xFF133100);
const background = Color(0xFFfffdf7);
const TextColor = Color(0xFFffffff);

class MyTheme {
  static final ThemeData defaultTheme = _buildMyTheme();

  //When using the Material package, the typography automatically defaults to the font family appropriate for the platform.
  static ThemeData _buildMyTheme() {
    final ThemeData base = ThemeData.light(useMaterial3: true);

    TextTheme startTextTheme;

    if (PlatformSpecific.useCupertino) {
      startTextTheme = Typography.blackCupertino;
    } else {
      startTextTheme = Typography.blackMountainView;
    }

    return base.copyWith(
      //accentColorBrightness: Brightness.dark,
      primaryColor: PrimaryColor,
      primaryColorDark: PrimaryColorDark,
      primaryColorLight: PrimaryColorLight,
      dividerTheme: base.dividerTheme.copyWith(color: SecondaryColorLight),
      //backgroundColor: SecondaryColor,
      splashColor: SecondaryColor,
      highlightColor: SecondaryColorLight,
      checkboxTheme: base.checkboxTheme.copyWith(
        fillColor: MaterialStateProperty.all(SecondaryColor),
        //checkColor: MaterialStateProperty.all(SecondaryColorDark)
      ),
      radioTheme: base.radioTheme.copyWith(
          fillColor: MaterialStateColor.resolveWith((states) => SecondaryColor),
          visualDensity: VisualDensity.compact),
      floatingActionButtonTheme: const FloatingActionButtonThemeData(
          foregroundColor: TextColor, backgroundColor: SecondaryColor),
      appBarTheme: AppBarTheme(
          surfaceTintColor:
              PlatformSpecific.useCupertino ? Colors.transparent : null,
          shadowColor: PlatformSpecific.useCupertino
              ? CupertinoColors.darkBackgroundGray
              : null,
          scrolledUnderElevation: PlatformSpecific.useCupertino ? .1 : null,
          color: PrimaryColor,
          toolbarHeight: PlatformSpecific.useCupertino ? 44 : null,
          titleTextStyle: const TextStyle(color: TextColor, fontSize: 20)),
      iconTheme: base.iconTheme.copyWith(color: SecondaryColorDark),
      primaryIconTheme:
          base.primaryIconTheme.copyWith(color: Colors.deepOrange),
      bottomSheetTheme:
          const BottomSheetThemeData(backgroundColor: PrimaryColorLight),
      elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(backgroundColor: PrimaryColor)),
      textButtonTheme: TextButtonThemeData(
          style: TextButton.styleFrom(foregroundColor: SecondaryColor)),
      canvasColor: SecondaryColorLight,
      scaffoldBackgroundColor: Colors.grey[400],
      snackBarTheme: base.snackBarTheme.copyWith(
          elevation: 16,
          backgroundColor: SecondaryColorDark,
          actionTextColor: TextColor,
          behavior: SnackBarBehavior.floating,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(4.0))),
      bannerTheme: base.bannerTheme.copyWith(
          backgroundColor: SecondaryColorDark,
          contentTextStyle: const TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.white,
            //backgroundColor: PrimaryColorLight
          ),
          elevation: 16),
      bottomNavigationBarTheme: base.bottomNavigationBarTheme.copyWith(
        backgroundColor: Colors.deepOrange,
        unselectedLabelStyle: const TextStyle(color: Colors.black),
      ),
      inputDecorationTheme: base.inputDecorationTheme.copyWith(
          //fillColor: SecondaryColor,
          border: OutlineInputBorder(
              borderSide: const BorderSide(
                color: SecondaryColorDark,
              ),
              borderRadius: BorderRadius.circular(10.0)),
          focusedBorder: OutlineInputBorder(
              borderSide: const BorderSide(
                color: Colors.deepOrangeAccent,
              ),
              borderRadius: BorderRadius.circular(10.0)),
          hintStyle: const TextStyle(color: SecondaryColorDark),
          labelStyle: const TextStyle(color: TextColor, fontSize: 18),
          filled: true,
          fillColor: SecondaryColorLight,
          helperStyle: const TextStyle(color: SecondaryColorDark)),
      datePickerTheme: base.datePickerTheme.copyWith(
          backgroundColor: PrimaryColorLight,
          headerBackgroundColor: PrimaryColorLight),
      timePickerTheme: base.timePickerTheme.copyWith(
          backgroundColor: PrimaryColorLight,
          dayPeriodTextColor: TextColor,
          hourMinuteTextColor: TextColor,
          hourMinuteColor: SecondaryColorDark,
          dayPeriodColor: SecondaryColorDark,
          entryModeIconColor: TextColor,
          dialHandColor: SecondaryColorLight,
          dialBackgroundColor: SecondaryColorDark,
          dialTextColor: TextColor),
      //this impacts the DatePicker as well - this is the color of the calendar background
      dialogBackgroundColor: PrimaryColorLight,
      //these also define the DatePicker ui
      colorScheme: base.colorScheme.copyWith(
        //primary will be the top of the DatePicker
        primary: SecondaryColorDark,
        secondary: SecondaryColor,
        //this is the background color of the Authenticator Signin and signup pages and will also
        //be the default background of Cards unless overridden by the CardTheme
        surface: background,
        onSurface: Colors.black,
      ),
      progressIndicatorTheme: base.progressIndicatorTheme.copyWith(
          linearTrackColor: Colors.deepOrangeAccent,
          circularTrackColor: Colors.deepOrangeAccent),
      popupMenuTheme: base.popupMenuTheme
          .copyWith(color: PrimaryColorLight, elevation: 8.0),
      cardTheme: base.cardTheme.copyWith(color: SecondaryColor),
      textSelectionTheme: const TextSelectionThemeData(
          cursorColor: SecondaryColorLight,
          selectionColor: SecondaryColorLight,
          selectionHandleColor: SecondaryColorLight),
      textTheme: startTextTheme.copyWith(
          labelMedium: const TextStyle(color: TextColor, fontSize: 16),
          labelSmall: const TextStyle(color: TextColor, fontSize: 12),
          headlineSmall: startTextTheme.headlineSmall
              ?.copyWith(fontSize: 16, color: SecondaryColorDark),
          bodySmall: startTextTheme.bodySmall
              ?.copyWith(fontSize: 14, color: Colors.black)),
    );
  }
}
Jordan-Nelson commented 1 year ago

This is what I am seeing for the background color Card widgets.

When useMaterial3 is true: cardTheme is used if defined. If not defined, colorScheme.surface is used. This is for all Material3 Card widgets, and not specific to the Authenticator.

When useMaterial3 is false: cardTheme is used if defined. colorScheme.surface doesn't seem to be used in any case.

It sounds like at this point you have figured out how to achieve what you were aiming to. Let me know if that is not the case.

We will look into improving the docs for this.

94Sip commented 1 year ago

Yes, I have figured it out, and I do appreciate you helping me reason thru this.

one small, important caveat: I had declared useMaterial3 in my theme, but I didn’t (originally) have cardTheme defined. So it was using colorScheme.surface. The code I posted above now has the cardTheme, and this seems to work. But please note that the iPad Authenticator is not using the cardTheme color - it is using the colorScheme.surface color.

With that said, I do agree with you that the docs could be improved, and I still think this is a bug. Here's why: based on what you are describing, my Theme is working just the opposite. I say that because I am clearly declaring "useMaterial3" but its behaving with colorScheme.surface for the Card. It is NOT using cardTheme even when I have cardTheme declared.

So I don’t think it is using useMaterial3 at all.

Jordan-Nelson commented 6 months ago

@94Sip Apologies for the delayed response. I attempted to reproduce this and I still see the same behavior as I noted previously. When cardTheme is defined, this is used. If not, ColorScheme.surface is used. If you are able to provide a minimal reproduction we can look into it.