PR

[Flutter]SVGをBottomNavigationBarItemに良い感じに表示する方法

Flutter

はじめに

久しぶりにFlutterの記事です。

SVGBottomNavigationBarItemに表示した際に起きたトラブル解消法を紹介しつつ、良い感じで表示する方法を紹介します。

環境

  • Flutter 2.8.1
  • flutter_svg: ^1.0.3

問題

BottomNavigationBarItemにSVGを表示したときに以下の3つの問題が発生しました。

  1. サイズがおかしい(表示されない)
  2. 選択時に色が変わらない
  3. 非選択時に色が変わらない

問題のコード

BottomNavigationBar(
        items: [
          const BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: SvgPicture.asset('assets/storage.svg'),
            label: 'SVG TEST',
          ),
        ],
...
),

問題の現象

サイズがおかしいですし、色も変わりません。

対策

対策方法は以下のとおりです。

  • SvgPicturewidthheightの指定
  • 選択時はBottomNavigationBarItemactiveIconSvgPicturecolorを指定したもの設定
  • 非選択時はBottomNavigationBarItemiconSvgPicturecolorを指定したもの設定

対策コード

BottomNavigationBar(
        items: [
          const BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: SvgPicture.asset(
              'assets/storage.svg',
              width: 24,
              height: 24,
              color: Colors.grey,
            ),
            label: 'SVG TEST',
            activeIcon: SvgPicture.asset(
              'assets/storage.svg',
              width: 24,
              height: 24,
              color: Colors.blue,
            ),
          ),
...
    ],

対策結果

正しく動作しています。

サンプルコード

とはいえ毎回このコードを書くのはめんどくさいです。

ということで、汎用的に使えるコードを作ってみました。

  • svg_bottom_navigation_bar_item.dart
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

class SvgBottomNavigationBarItem extends BottomNavigationBarItem {
  const SvgBottomNavigationBarItem(
    BuildContext context, {
    required Widget icon,
    String? label,
    Widget? activeIcon,
    Color? backgroundColor,
    String? tooltip,
  }) : super(
          icon: icon,
          label: label,
          activeIcon: activeIcon,
          backgroundColor: backgroundColor,
          tooltip: tooltip,
        );

  factory SvgBottomNavigationBarItem.asset(
    BuildContext context,
    String assetName, {
    double size = 24,
    String? label,
    Color? backgroundColor,
    String? tooltip,
  }) =>
      SvgBottomNavigationBarItem(
        context,
        icon: SvgPicture.asset(
          assetName,
          height: size,
          width: size,
          color: Theme.of(context).unselectedWidgetColor,
        ),
        label: label,
        activeIcon: SvgPicture.asset(
          assetName,
          height: size,
          width: size,
          color: Theme.of(context).primaryColor,
        ),
        backgroundColor: backgroundColor,
        tooltip: tooltip,
      );

  factory SvgBottomNavigationBarItem.network(
    BuildContext context,
    String url, {
    double size = 24,
    String? label,
    Color? backgroundColor,
    String? tooltip,
  }) =>
      SvgBottomNavigationBarItem(
        context,
        icon: SvgPicture.network(
          url,
          height: size,
          width: size,
          color: Theme.of(context).unselectedWidgetColor,
        ),
        label: label,
        activeIcon: SvgPicture.network(
          url,
          height: size,
          width: size,
          color: Theme.of(context).primaryColor,
        ),
        backgroundColor: backgroundColor,
        tooltip: tooltip,
      );
}
  • main.dart
import 'package:flutter/material.dart';
import 'package:flutter_app/svg_bottom_navigation_bar_item.dart';
import 'package:flutter_svg/flutter_svg.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_currentIndex',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          const BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          SvgBottomNavigationBarItem.asset(
            context,
            'assets/storage.svg',
            label: 'SVG Assets',
          ),
          SvgBottomNavigationBarItem.network(
            context,
            'https://icooon-mono.com/i/icon_11482/icon_114820.svg',
            label: 'SVG Network',
          ),
        ],
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        type: BottomNavigationBarType.fixed,
        currentIndex: _currentIndex,
      ),
    );
  }
}

結果

SVGがきれいに表示にされています。Networkから取得しても問題なさそうです。

※ただしネットワークから拾ってる画像が<style>属性があるのでエラーが出ているので、実際使う際は<style>属性がないSVGを利用してください。

さいごに

<style>属性問題、いい加減なんとかしてほしい。。。

おすすめ参考書

リンク

GitHub - nobushiueshi/flutter_widget_of_the_week
Contribute to nobushiueshi/flutter_widget_of_the_week development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました