Repository: flutter
Issue: Cupertino in dark mode is too low-contrast and theme APIs are missing
Labels: framework,a: accessibility,f: cupertino,team-design,triaged-design

## Use case

I'm writing an app with iOS style using Flutter's Cupertino library.  I want the app to automatically switch between light and dark modes.

## Proposal

1. Fix the Cupertino widgets to be usable in dark mode.
2. Add support to Flutter for dark mode themes.
3. Provide a working example somewhere in the documentation.

## Existing APIs with Missing or Undocumented Dark Mode Support

- The [`MaterialApp `](https://api.flutter.dev/flutter/material/MaterialApp-class.html) constructor takes both `theme` and `darkTheme` arguments.  The [`CupertinoApp `](https://api.flutter.dev/flutter/cupertino/CupertinoApp-class.html) constructor takes only `theme`.
- [`CupertinoTheme `](https://api.flutter.dev/flutter/cupertino/CupertinoTheme-class.html) takes a single [`CupertinoThemeData `](https://api.flutter.dev/flutter/cupertino/CupertinoThemeData-class.html) which can represent either a light or a dark color scheme, but not both.  [`Theme `](https://api.flutter.dev/flutter/material/Theme-class.html) and [`ThemeData `](https://api.flutter.dev/flutter/material/ThemeData-class.html) have the same limitation.
- [`CupertinoDynamicColor`](https://api.flutter.dev/flutter/cupertino/CupertinoDynamicColor-class.html) seems able to choose specific colors for light and dark modes.  The docs are very difficult to understand.  For example, it talks about "manually resolving a color" but I have no idea what this means.
- One cannot use [`MediaQuery.of(ctx).platformBrightness`](https://api.flutter.dev/flutter/widgets/MediaQuery-class.html) to pick the theme passed to `CupertinoApp` because there is no parent `MediaQuery` widget.  Sadly, one cannot just add a new `MediaQuery` widget above the `CupertinoApp` widget because the code to query the system's settings is hidden in [`widgets/app.dart:_MediaQueryFromWindow`](https://github.com/flutter/flutter/blob/e7b7ebc066c1b2a5aa5c19f8961307427e0142a6/packages/flutter/lib/src/widgets/app.dart#L1662).
- Workaround code for issue #48438

## Mostly-Working Example
Problems in Dark Mode:
- Disabled button text is unreadable.
- Picker background is faint.  It looks like a label, not a picker.
- Slider track is very faint.
- Edit box placeholder text is too faint to see.
- Alert dialog border is invisible (#80921)
- Keyboard is too bright on Android.

<details> 
<summary>code sample</summary> 

```dart
// main.dart
import 'package:flutter/cupertino.dart'
    show
    showCupertinoDialog,
    CupertinoAlertDialog,
    CupertinoApp,
    CupertinoButton,
    CupertinoDatePicker,
    CupertinoDialogAction,
    CupertinoIcons,
    CupertinoNavigationBar,
    CupertinoPage,
    CupertinoPageScaffold,
    CupertinoPicker,
    CupertinoSlider,
    CupertinoSwitch,
    CupertinoTextField,
    CupertinoTheme,
    CupertinoThemeData;
import 'package:flutter/foundation.dart'
    show
    debugBrightnessOverride,
    kReleaseMode;
import 'package:flutter/widgets.dart'
    show
    runApp,
    Builder,
    BuildContext,
    Center,
    Color,
    Column,
    Container,
    DefaultTextStyle,
    EdgeInsets,
    Icon,
    Key,
    MainAxisSize,
    MediaQuery,
    MediaQueryData,
    Page,
    Row,
    Spacer,
    State,
    StatefulWidget,
    StatelessWidget,
    Text,
    TextAlign,
    TextEditingController,
    Navigator,
    Widget,
    WidgetsBinding,
    WidgetsBindingObserver;
import 'dart:ui' show Brightness;

// Copied from flutter/lib/src/widgets/app.dart:_MediaQueryFromWindow .
class MediaQueryFromWindow extends StatefulWidget {
  const MediaQueryFromWindow({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  MediaQueryFromWindowsState createState() => MediaQueryFromWindowsState();
}

class MediaQueryFromWindowsState extends State<MediaQueryFromWindow>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeAccessibilityFeatures() {
    setState(() {});
  }

  @override
  void didChangeMetrics() {
    setState(() {});
  }

  @override
  void didChangeTextScaleFactor() {
    setState(() {});
  }

  @override
  void didChangePlatformBrightness() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    MediaQueryData data = MediaQueryData.fromWindow(
        WidgetsBinding.instance.window);
    if (!kReleaseMode) {
      data = data.copyWith(platformBrightness: debugBrightnessOverride);
    }
    return MediaQuery(
      data: data,
      child: widget.child,
    );
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
}

Page rootPage() =>
    CupertinoPage(
      title: 'Root Title',
      child: CupertinoPageScaffold(
        navigationBar: CupertinoNavigationBar(),
        child: Text('Root'),
      ),
    );

class CupertinoWidgetsPageWidget extends StatelessWidget {
  TextEditingController controller = new TextEditingController(
      text: "contents1");

  @override
  Widget build(BuildContext ctx) =>
      CupertinoPageScaffold(
        navigationBar: CupertinoNavigationBar(
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              CupertinoButton(
                padding: EdgeInsets.symmetric(
                    horizontal: 4.0),
                child: Text(
                  'Disabled1',
                  textAlign: TextAlign.center,
                ),
              ),
              CupertinoButton(
                padding: EdgeInsets.symmetric(
                    horizontal: 4.0),
                onPressed: () => {},
                child: Text(
                  'Action1',
                  textAlign: TextAlign.center,
                ),
              ),

            ],
          ),
        ),
        child: Column(
          children: [
            Text('Text1'),
            Icon(
              CupertinoIcons.right_chevron,
              size: 24.0,
              semanticLabel: 'Chevron',
            ),
            CupertinoButton(
              child: Text('Enabled Button'),
              onPressed: () =>
                  showCupertinoDialog(
                    context: ctx,
                    builder: (BuildContext ctx) =>
                        CupertinoAlertDialog(
                          title: Text('Alert Title'),
                          content: Text('Content1'),
                          actions: [
                            CupertinoDialogAction(
                              isDefaultAction: false,
                              isDestructiveAction: false,
                              child: Text('Disabled1'),
                            ),
                            CupertinoDialogAction(
                              isDefaultAction: false,
                              isDestructiveAction: false,
                              onPressed: () =>
                                  Navigator.pop(
                                      ctx, false),
                              child: Text('Action1'),
                            ),
                            CupertinoDialogAction(
                              isDefaultAction: false,
                              isDestructiveAction: true,
                              onPressed: () =>
                                  Navigator.pop(
                                      ctx, false),
                              child: Text('Destructive1'),
                            ),
                            CupertinoDialogAction(
                              isDefaultAction: true,
                              isDestructiveAction: false,
                              onPressed: () =>
                                  Navigator.pop(
                                      ctx, false),
                              child: Text('Cancel'),
                            ),
                          ],
                        ),
                  ),
            ),
            CupertinoButton(
                child: Text('Disabled Button')),
            Row(children: [
              CupertinoSwitch(value: true),
              CupertinoSwitch(value: false),
              CupertinoSwitch(
                value: true,
                onChanged: (x) => {},
              ),
              CupertinoSwitch(
                value: false,
                onChanged: (x) => {},
              ),
            ]),
            CupertinoPicker(
              itemExtent: 30.0,
              onSelectedItemChanged: (x) => {},
              children: [
                Text('Item1'),
                Text('Item2'),
                Text('Item3'),
                Text('Item4'),
              ],
            ),
            CupertinoSlider(
              value: 0.0,
              divisions: 3,
              onChanged: (x) => {},
            ),
            Container(
              height: 100.0,
              child:
              CupertinoDatePicker(
                onDateTimeChanged: (x) => {},
              ),
            ),
            CupertinoTextField(
                placeholder: "placeholder1"),
            CupertinoTextField(
                controller: this.controller),
            Spacer(),
          ],
        ),
      );
}

Page cupertinoWidgetsPage() =>
    CupertinoPage(
        title: 'Title1',
        child: CupertinoWidgetsPageWidget());

class AppWidget extends StatelessWidget {
  @override
  Widget build(BuildContext ctx) {
    final isLight = MediaQuery
        .of(ctx)
        .platformBrightness == Brightness.light;
    final cupertinoThemeData = isLight ? CupertinoThemeData(
      // Light
      primaryColor: Color(0xff664600),
      barBackgroundColor: Color(0xffFFF0D0),
      scaffoldBackgroundColor: Color(0xffFFFFFF),
    ) : CupertinoThemeData(
      // Dark
      primaryColor: Color(0xffFFF0D0),
      barBackgroundColor: Color(0xff664600),
      // Has no effect on any Cupertino* widgets.
      primaryContrastingColor: Color(0xffFF0000),
    );
    return CupertinoApp(
      theme: cupertinoThemeData,
      title: 'App1',
      home: Builder(builder: (BuildContext ctx) =>
          DefaultTextStyle(
            style: CupertinoTheme
                .of(ctx)
                .textTheme
                .textStyle,
            child: Navigator(
              pages: [rootPage(), cupertinoWidgetsPage()],
              onPopPage: (route, result) => true,
            ),
          ),
      ),
    );
  }
}

void main() {
  runApp(MediaQueryFromWindow(child: AppWidget()));
}
``` 

</details>


| iOS dark | iOS light |
| --------------- | --------------- |
![Simulator Screen Shot - iPhone 11 - 2021-04-21 at 22 57 25](https://user-images.githubusercontent.com/520739/115663522-7b0bf300-a2f5-11eb-861e-826a5cd2028f.png) | ![Simulator Screen Shot - iPhone 11 - 2021-04-21 at 22 58 17](https://user-images.githubusercontent.com/520739/115663646-ad1d5500-a2f5-11eb-8eeb-268e379b2a7b.png) |
![Simulator Screen Shot - iPhone 11 - 2021-04-21 at 22 57 46](https://user-images.githubusercontent.com/520739/115663550-8bbc6900-a2f5-11eb-8a5c-b30be113a407.png) | ![Simulator Screen Shot - iPhone 11 - 2021-04-21 at 22 58 23](https://user-images.githubusercontent.com/520739/115663659-b1497280-a2f5-11eb-832b-bb62d2d228bd.png) |

| Android dark | Android light |
| --------------- | --------------- |
![Screenshot_1619071226](https://user-images.githubusercontent.com/520739/115663610-a262c000-a2f5-11eb-92cd-9a5998d259f2.png) | ![Screenshot_1619071175](https://user-images.githubusercontent.com/520739/115663797-e5249800-a2f5-11eb-9391-90089f98d016.png) |
![Screenshot_1619071230](https://user-images.githubusercontent.com/520739/115663623-a7277400-a2f5-11eb-9c46-251f8626a56f.png) | ![Screenshot_1619071188](https://user-images.githubusercontent.com/520739/115663811-e8b81f00-a2f5-11eb-842b-3d9cef212dfe.png) |

EDIT 1: Mention `MediaQuery.of(ctx).platformBrightness`.