Содержание

Настраиваем тему диалога выбора даты в Flutter

Flutter - невероятно гибкий инструмент, который позволяет вам создавать кросс-платформенные приложения с уникальным дизайном.

Однако, такая гибкость требует порой дополнительных действий. Рассмотрим это на примере настройки темы стандартного диалога выбора даты в Material Components.

Использование primarySwatch

Обычно, самый простой способ установить цвет основных элементов - это использовать поле primarySwatch объекта ThemeData. В документации указано, для каких именно элементов устанавливается цвет по-умолчанию с его помощью:

primarySwatch
primarySwatch - used to configure default values for several fields, including: primaryColor, primaryColorBrightness, primaryColorLight, primaryColorDark, toggleableActiveColor, accentColor, colorScheme, secondaryHeaderColor, textSelectionColor, backgroundColor, and buttonColor.

Продемонстрируем это на примере:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text('Open calendar'),
              onPressed: _showCalendar,
            ),
          ],
        ),
      ),
    );
  }

  void _showCalendar() {
    showDatePicker(
      context: context,
      initialDate: DateTime.now(),
      firstDate: DateTime(2019),
      lastDate: DateTime(2033),
      helpText: 'ВЫБЕРИТЕ ДАТУ',
      fieldLabelText: 'Введите дату',
    );
  }
}

При вызове диалога выбора даты мы увидим следующее:

Диалог выбора даты с установленным значением primarySwatch

Можно увидеть, что и аппбар экрана, и верхняя часть диалога, имеет выбранный нами зелёный цвет.

Но что если мы хотим, чтобы разные элементы интерфейса по-умолчанию были разного цвета?

Гибкая настройка темы

Предположим, что мы хотим, чтобы аппбар был по-прежнему зелёным, кнопки - фиолетовыми, а верхняя часть календаря - янтарно-жёлтой.

Для начала настроим объект ThemeData в нашем приложении:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primaryColor: Colors.green,
        buttonTheme: ButtonThemeData(
          buttonColor: Colors.indigo,
          textTheme: ButtonTextTheme.primary,
        ),
        primaryTextTheme: TextTheme(
          button: TextStyle(
            color: Colors.green,
            fontWeight: FontWeight.w600,
          ),
        ),
        dialogTheme: DialogTheme(
          backgroundColor: Colors.amber,
        ),
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

И посмотрим на результат:

Экран Диалог
./images/screen.png ./images/dialog_with_theme.png

Упс! Не совсем то, что мы ожидали увидеть.

Аппбар экрана и кнопка стали нужного нам цвета, а вот про диалог такого не скажешь: почему-то жёлтым стал фон текста, а не заголовочной части.

Если внимательно посмотреть в конструктор DialogTheme, то можно увидеть, что там нет специального поля для заголовочной части диалога:

1
2
3
4
5
6
7
DialogTheme({
    Color backgroundColor,
    double elevation,
    ShapeBorder shape,
    TextStyle titleTextStyle,
    TextStyle contentTextStyle,
});

Как же быть?

Во-первых, нужно удалить часть с dialogTheme из ThemeData, раз уж она не делает то, что нужно нам.

Во-вторых, нужно воспользоваться параметром builder, который есть в showDatePicker. Он принимает функцию с двумя аргументами, возвращающую виджет:

1
Widget Function(BuildContext, Widget) builder

Изменим вызов функции showDatePicker:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
showDatePicker(
  context: context,
  initialDate: DateTime.now(),
  firstDate: DateTime(2019),
  lastDate: DateTime(2033),
  helpText: 'ВЫБЕРИТЕ ДАТУ',
  fieldLabelText: 'Введите дату',
  builder: (BuildContext context, Widget child) {
    return Theme(
      data: ThemeData(
        colorScheme: ColorScheme.light(primary: Colors.amber),
      ),
      child: child,
    );
  },
);

Совсем другое дело!