🏥

【Flutter】ClipRRectがうまく効かない時

2024/08/27に公開

はじめに

FlutterでWidgetの角を丸くしたい時によく使うのがClipRRectですが、たまにうまく角を丸くできなくてなんでかわからないことありませんか?
その場合の解決策の一つをご紹介します。

結論

角丸をつけたい対象のWidgetがInkWellで囲まれている場合
→ MaterialでWrapしてあげることでClipRRectが効くようになる。

MaterialInkWellの公式ドキュメントにも記載されてます。

そのほかの場合
→ コメントで教えてください!追記します。

InkWellが隠れている例

ListTileウィジェットを使ったことがありますか?
縦並びのリストを作る際の1要素として重宝します。

実はこのListTileウィジェットはClipRRectが直接効かないんです。

例えばこんなコードがあるとします。

class MyPage extends StatelessWidget {
  const MyPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('My Page'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ListView.builder(
            itemCount: 20,
            itemBuilder: (context, index) {
              final opacity = (0.5 + (index * 0.05)).clamp(0, 1).toDouble();
              return ListTile(
                title: Text('ListTile No. ${index + 1}'),
                subtitle: const Text('ListTile Subtitle'),
                leading: const Icon(Icons.list),
                trailing: const Icon(Icons.more_vert),
                tileColor: Theme.of(context)
                    .colorScheme
                    .secondary
                    .withOpacity(opacity),
              );
            }),
      ),
    );
  }
}

ビルドしてみるとこんな感じ

まあ、ClipRRectすら書いてないので、角丸はありません。次にClipRRectをListViewに掛けてみたり、ListTileに掛けてみたりしても角丸はつかないのです。お手元でお試しください。

ClipRRectをListViewに掛けた場合
ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: ListView.builder(
              itemCount: 20,
              itemBuilder: (context, index) {
                final opacity = (0.5 + (index * 0.05)).clamp(0, 1).toDouble();
                return ListTile(
                  title: Text('ListTile No. ${index + 1}'),
                  subtitle: const Text('ListTile Subtitle'),
                  leading: const Icon(Icons.list),
                  trailing: const Icon(Icons.more_vert),
                  tileColor: Theme.of(context)
                      .colorScheme
                      .secondary
                      .withOpacity(opacity),
                );
              }),
        ),
ClipRRectをListTileにかけた場合
Padding(
        padding: const EdgeInsets.all(8.0),
        child: ListView.builder(
            itemCount: 20,
            itemBuilder: (context, index) {
              final opacity = (0.5 + (index * 0.05)).clamp(0, 1).toDouble();
              return ClipRRect(
                borderRadius: BorderRadius.circular(8),
                child: ListTile(
                  title: Text('ListTile No. ${index + 1}'),
                  subtitle: const Text('ListTile Subtitle'),
                  leading: const Icon(Icons.list),
                  trailing: const Icon(Icons.more_vert),
                  tileColor: Theme.of(context)
                      .colorScheme
                      .secondary
                      .withOpacity(opacity),
                ),
              );
            }),
      ),

理由は先ほど述べた通り、角丸をつけたい対象のWidgetがInkWellで囲まれている場合角丸がつかないからです。
InkWellなんて無くない?と思ったあなた、ListTileウィジェットの内部実装を見てみましょう。(そんなに潜らないので安心して)

ListTile classのbuildメソッドを覗いてみると、returnの直後がInkWellから始まっていることがわかると思います。
これがListTileが角丸がつかない原因でした。

それでは先ほど述べた通りMaterialWidgetで囲んでみましょう。
今回はPaddingの内側にClipRRectをつけてみました。

Padding(
        padding: const EdgeInsets.all(8.0),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: Material(
            child: ListView.builder(
                itemCount: 20,
                itemBuilder: (context, index) {
                  final opacity = (0.5 + (index * 0.05)).clamp(0, 1).toDouble();
                  return ListTile(
                    title: Text('ListTile No. ${index + 1}'),
                    subtitle: const Text('ListTile Subtitle'),
                    leading: const Icon(Icons.list),
                    trailing: const Icon(Icons.more_vert),
                    tileColor: Theme.of(context)
                        .colorScheme
                        .secondary
                        .withOpacity(opacity),
                  );
                }),
          ),
        ),
      ),

無事に角丸がつきましたね。

ちなみにFlutterにはInkwellによく似た名前のWidgetとしてInkResponseとInkというWidgetがありますが、InkWellとどのように違うのかはこちらの記事で詳しく解説されているので、気になる方はご覧ください。

おわりに

2024年は技術発信も頑張ろうと思っているので、記事が参考になった方は記事とGitHubのいいね(スター)とフォローをしていただけると励みになります!
最後まで読んでいただきありがとうございました✨

https://github.com/miyasic

Discussion