はじめに
ButtonのonPressに条件によってnullを渡して非活性にさせたいことがあると思います。
本来はnullを渡すとボタンがタップできなくなるのに加えて見た目も透明度が落ちて薄暗い状態になります。
しかし、何故かいくらnullを渡しても見た目が変わらない事象に遭遇しました。
その解決方法を解説します。
記事の対象者
- ボタンを非活性にさせたいのに見た目が変わらない事でお困りの方
記事を執筆時点での筆者の環境
[✓] Flutter (Channel stable, 3.22.1, on macOS 14.3.1 23D60 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.3)
[✓] VS Code (version 1.89.1)
1. 原因
ButtonStyle
でbackgroundColor
やforegroundColor
をWidgetStateProperty.all
で指定してしまった事が原因でした。
つまり、どんな状態でも同じ色になるように設定してしまいました。
ButtonStyle createBadPrimaryButtonStyle(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return ButtonStyle(
fixedSize: WidgetStateProperty.all(const Size.fromHeight(48)),
backgroundColor: WidgetStateProperty.all(colorScheme.primary),
foregroundColor: WidgetStateProperty.all(colorScheme.onPrimary),
textStyle: WidgetStateProperty.all(
Theme.of(context)
.textTheme
.labelLarge!
.copyWith(fontWeight: FontWeight.w600),
),
);
}
2. 解決策
非活性の場合とそうではない場合でそれぞれの状態を指定すれば解決できます。
ButtonStyle createPrimaryButtonStyle(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return ButtonStyle(
// ボタンの高さを48に固定
fixedSize: WidgetStateProperty.all(const Size.fromHeight(48)),
// NOTE: スタイルを自作すると、ディセーブルの時の値を手動で設定する必要がある
backgroundColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return colorScheme.primary.withOpacity(0.7);
}
return colorScheme.primary;
},
),
// NOTE: スタイルを自作すると、ディセーブルの時の値を手動で設定する必要がある
foregroundColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return colorScheme.onPrimary.withOpacity(0.7);
}
return colorScheme.onPrimary;
},
),
textStyle: WidgetStateProperty.all(
Theme.of(context)
.textTheme
.labelLarge!
.copyWith(fontWeight: FontWeight.w600),
),
);
}
3. サンプル
コード
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('全部にnullを渡して非活性にしています'),
// gapというパッケージ使ってます
const Gap(20),
const ElevatedButton(
onPressed: null,
child: Text('デフォルトのボタン'),
),
const Gap(20),
ElevatedButton(
onPressed: null,
style: createPrimaryButtonStyle(context),
child: const Text('非活性部分もちゃんと定義したボタン'),
),
const Gap(20),
ElevatedButton(
onPressed: null,
style: createBadPrimaryButtonStyle(context),
child: const Text('非活性部分は定義していないボタン'),
),
],
),
),
),
);
}
}
createPrimaryButtonStyle
とcreateBadPrimaryButtonStyle
はグローバル定義しています。
ここでの掲載は省略しています。
スクリーンショット
終わりに
言われてみれば確かに.allでしていたな、と思う事でしたが当初は分からず小一時間悩んでいました😰
同じ悩みに遭遇されている方の一助となれば幸いです。
参考記事