2021/2/10 Use themes to share colors and font stylesの訳

Use themes to share colors and font styles

アプリ全体で色とフォントスタイルを共有するには、テーマ(theme)を使用します。

アプリ全体のテーマ(theme)を定義するか、アプリケーションの特定の部分の色とフォントスタイルを定義するThemeウィジェットを使用できます。

実際、アプリ全体のテーマは、MaterialAppによってアプリのルートで作成されたThemeウィジェットにすぎません。

テーマを定義したら、独自のウィジェット内で使用します。

Flutterのマテリアルウィジェットは、テーマを使用して、AppBar、ボタン、チェックボックスなどの背景色とフォントスタイルを設定します。


Creating an app theme

アプリ全体でテーマを共有するには、MaterialAppコンストラクターにThemeDataを提供します。

テーマ(theme)が提供されていない場合、Flutterは自動的にデフォルトのthemeを作成します。

MaterialApp(
  title: title,
  theme: ThemeData(
    // Define the default brightness and colors.
    brightness: Brightness.dark,
    primaryColor: Colors.lightBlue[800],
    accentColor: Colors.cyan[600],

    // Define the default font family.
    fontFamily: 'Georgia',

    // Define the default TextTheme. Use this to specify the default
    // text styling for headlines, titles, bodies of text, and more.
    textTheme: TextTheme(
      headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
      headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
      bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
    ),
  )
);

定義できるすべての色とフォントについては、ThemeDataのドキュメントを参照してください。


Themes for part of an application

アプリケーションの一部で、アプリ全体のテーマをオーバーライド(上書き)したい時は、上書きしたいアプリのセクションをThemeウィジェットでラップします。

これを実現する方法として、(1)ユニークな新しいThemeDataを作成する方法と、(2)親Themeを拡張(extend)する方法の2つがあります。


(1)Creating unique ThemeData

アプリケーションの色やフォントスタイルを継承したくない場合は、ThemeData()インスタンスを作成し、それをテーマウィジェットに渡します。

Theme(
  // Create a unique theme with "ThemeData"
  data: ThemeData(
    accentColor: Colors.yellow,
  ),
  child: FloatingActionButton(
    onPressed: () {},
    child: Icon(Icons.add),
  ),
);

ラップするだけ。シンプル!笑

ただ何も考えずにいろんな箇所でやるとゴチャゴチャして保守性が下がりそうなので、できるだけ効率的に、スタイルを当てる方法は考えるべきですね。


(2)Extending the parent theme

多くの場合、すべてをオーバーライドするのではなく、親テーマを拡張する方が理にかなっています。 これは、copyWith()メソッドを使用して処理できます。

Theme(
  // Find and extend the parent theme using "copyWith". See the next
  // section for more info on `Theme.of`.
  data: Theme.of(context).copyWith(accentColor: Colors.yellow),
  child: FloatingActionButton(
    onPressed: null,
    child: Icon(Icons.add),
  ),
);

「copyWith」を使用して親Themeを見つけて拡張します。 `Theme.of`の詳細については、次のセクションを参照してください。

copyWithメソッドはThemeDataクラスだけではなくいろいろなクラスで用意されているメソッドですが、指定したフィールドの値だけを上書きした新しいインスタンスを返すメソッドです。

この例ではTheme.of(context)で取得したThemeDataインスタンスのaccentColorフィールドのみをColors.yellowに変更した新しいThemeDataインスタンスを、dataフィールドに設定しています。はい。


Using a Theme

テーマを定義したので、Theme.of(context)メソッドを使用して、ウィジェットのbuild()メソッド内でテーマを使用します。

Theme.of(context)メソッドはウィジェットツリーを検索(contextに対応したウィジェットの祖先ウィジェットを検索していく)し、ツリー内で最も近いテーマを返します。

ウィジェットの上にスタンドアロンのテーマが定義されている場合は、それが返されます。 そうでない場合は、アプリの(MaterialAppの)ThemeDataが返されます。

実際、下記のサンプルでは、FloatingActionButtonがこの手法を使用してaccentColorを見つけます。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        // Define the default brightness and colors.
        brightness: Brightness.dark,
        primaryColor: Colors.lightBlue[800],
        accentColor: Colors.cyan[600],

        // Define the default font family.
        fontFamily: 'Georgia',

        // Define the default TextTheme. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: MyHomePage(
        title: appName,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  MyHomePage({Key key, @required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Container(
          color: Theme.of(context).accentColor,
          child: Text(
            'Text with a background color',
            style: Theme.of(context).textTheme.headline6,
          ),
        ),
      ),
      floatingActionButton: Theme(
        data: Theme.of(context).copyWith(
          colorScheme:
              Theme.of(context).colorScheme.copyWith(secondary: Colors.yellow),
        ),
        child: FloatingActionButton(
          onPressed: () {},
          child: Icon(
            Icons.add,
            color: Colors.red,
          ),
        ),
      ),
    );
  }
}

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        // Define the default brightness and colors.
        colorScheme:ColorScheme.light(
          secondary:Colors.lime,
        ),
        brightness: Brightness.light,
        primaryColor: Colors.lightBlue[800],
        accentColor: Colors.cyan[600],

        // Define the default font family.
        fontFamily: 'Georgia',

        // Define the default TextTheme. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: MyHomePage(
        title: appName,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  MyHomePage({Key key, @required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Container(
          color: Theme.of(context).accentColor,
          child: Text(
            'Text with a background color',
            style: Theme.of(context).textTheme.headline6,
          ),
        ),
      ),
      floatingActionButton: Theme(
        data: Theme.of(context).copyWith(
          colorScheme:
              Theme.of(context).colorScheme.copyWith(secondary: Colors.purpleAccent),
        ),
        child: FloatingActionButton(
         //↓この行によりフローティングアクションボタンはライム色(Colors.lime)になる。
         //↓この行をコメントアウトすると、ラップしているThemeウィジェットで指定されている
         //Colors.purpleAccent(紫色)になる。
          backgroundColor: Theme.of(context).colorScheme.secondary,
          onPressed: () {},
          child: Icon(
            Icons.add,
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

MaterialAppのthemeフィールドでThemeData.colorScheme.secondaryを

Colors.lime

に設定した。


フローティングアクションボタン(FAB)を押すとFABの色が変わるサンプル。

// lib/main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        // Define the default brightness and colors.
        colorScheme:ColorScheme.light(
          secondary:Colors.lime,
        ),
        brightness: Brightness.light,
        primaryColor: Colors.lightBlue[800],
        accentColor: Colors.cyan[600],

        // Define the default font family.
        fontFamily: 'Georgia',

        // Define the default TextTheme. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: MyHomePage(
        title: appName,
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, @required this.title}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  String colorOfFAB='lime';

  void _changeColorOfFAB(){
    setState((){
      colorOfFAB = colorOfFAB == 'lime' ? 'purple' : 'lime';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Container(
          color: Theme.of(context).accentColor,
          child: Text(
            'Text with a background color',
            style: Theme.of(context).textTheme.headline6,
          ),
        ),
      ),
      floatingActionButton: Theme(
        data: Theme.of(context).copyWith(
          colorScheme:
              Theme.of(context).colorScheme.copyWith(secondary: Colors.purpleAccent),
        ),
        child: FloatingActionButton(
          backgroundColor: colorOfFAB == 'lime'
              ? Theme.of(context).colorScheme.secondary
              : null,
          onPressed: _changeColorOfFAB,
          child: Icon(
            Icons.add,
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

とりあえず動くコード書いているだけなので、状態変更ロジックなどはもう少し考えた方が良いと思います笑


FABを押すとダークモードのオンオフを切り替えるサンプル。

こうするとコールバック(setDarkMode())のバケツリレーが必要。

// lib.main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  bool darkMode = false;

  void setDarkMode(){
    setState((){
      darkMode =! darkMode;
    });
  }

  @override
  Widget build(BuildContext context) {
    final appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        // Define the default brightness and colors.
        brightness: darkMode ? Brightness.dark : Brightness.light,
        primaryColor: Colors.lightBlue[800],
        accentColor: Colors.cyan[600],

        // Define the default font family.
        fontFamily: 'Georgia',

        // Define the default TextTheme. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: MyHomePage(
        title: appName,
        setDarkMode:setDarkMode,
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  final setDarkMode;

  MyHomePage({Key key, @required this.title, this.setDarkMode}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Container(
          color: Theme.of(context).accentColor,
          child: Text(
            'Text with a background color',
            style: Theme.of(context).textTheme.headline6,
          ),
        ),
      ),
      floatingActionButton: Theme(
        data: Theme.of(context).copyWith(
          colorScheme:
          Theme.of(context).colorScheme.copyWith(secondary: Colors.yellow),
        ),
        child: FloatingActionButton(
          onPressed: setDarkMode,
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

FABを押すとダークモードのオンオフを切り替えるサンプルパート2。

こうすればバケツリレーは必要無くなる。

// lib.main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appName = 'Custom Themes';

    return MaterialApp(
      title: appName,
      theme: ThemeData(
        // Define the default brightness and colors.
        brightness: Brightness.dark,
        primaryColor: Colors.pink[800],
        accentColor: Colors.pink[200],

        // Define the default font family.
        fontFamily: 'Georgia',

        // Define the default TextTheme. Use this to specify the default
        // text styling for headlines, titles, bodies of text, and more.
        textTheme: TextTheme(
          headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
          headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
          bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
        ),
      ),
      home: MyHomePage(
        title: appName,
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, @required this.title}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool darkMode = false;

  @override
  Widget build(BuildContext context) {
    return Theme(
      data:darkMode ? ThemeData.dark() : ThemeData.light(),
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Container(
            color: Theme.of(context).accentColor,
            child: Text(
              'Text with a background color',
              style: Theme.of(context).textTheme.headline6,
            ),
          ),
        ),
        floatingActionButton: Theme(
          data: Theme.of(context).copyWith(
            colorScheme:
            Theme.of(context).colorScheme.copyWith(secondary: Colors.tealAccent),
          ),
          child: FloatingActionButton(
            onPressed: ()=>setState((){
              darkMode = !darkMode;
            }),
            child: Icon(Icons.add),
          ),
        ),
      ),
    );
  }
}

 


 

 

 

 

参考

https://flutter.dev/docs/cookbook/design/themes


 

 

子孫ウィジェットにテーマ(Theme)を適用します。

テーマ(Theme)は、アプリケーション内の色やタイポグラフィなどを表現します。

子孫ウィジェットはTheme.ofメソッドを使って現在のテーマのThemeDataオブジェクトを得ます。

 

 

参考

https://api.flutter.dev/flutter/material/Theme-class.html

コメントを残す

メールアドレスが公開されることはありません。