☣️

_Assertionerror

2023/02/12に公開

上タブを作っていたときにハマった!

Tabbarを作成していたときに、Widgetのエラーが出てきた!
やったことは、ドキュメントのソースコードを書き換えて使っただけだった!
https://api.flutter.dev/flutter/material/TabBar-class.html

これを作ります

こちらのコードを書き換えて使った!

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  static const String _title = 'Flutter Code Sample';

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

/// [AnimationController]s can be created with `vsync: this` because of
/// [TickerProviderStateMixin].
class _MyStatefulWidgetState extends State<MyStatefulWidget>
    with TickerProviderStateMixin {
  late TabController _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TabBar Widget'),
        bottom: TabBar(
          controller: _tabController,
          tabs: const <Widget>[
            Tab(
              icon: Icon(Icons.cloud_outlined),
            ),
            Tab(
              icon: Icon(Icons.beach_access_sharp),
            ),
            Tab(
              icon: Icon(Icons.brightness_5_sharp),
            ),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: const <Widget>[
          Center(
            child: Text("It's cloudy here"),
          ),
          Center(
            child: Text("It's rainy here"),
          ),
          Center(
            child: Text("It's sunny here"),
          ),
        ],
      ),
    );
  }
}

こちらのコードを書きかえて、TextWidgetから、WidgetClassに変更したが、buildができなかった?
原因は、DefaultTabControllerの方ではなくて、自分で設定できる方のTabControllerを使ったことが原因らしい?
しかし、新しくプロジェクトを作成してドキュメントのコードを実行してもエラーにならなかった?

エラーを解消した方法

優秀なメンターさんからのアドバイスによると、TabBarの中にonTapがないかららしい!
onTap: ()の中に、indexを書いて、_tabController.animateTo()の中にもindexを書くと、buildが通った???

return Scaffold(
      appBar: AppBar(
        centerTitle: true, // Androidの場合タイトルを中央寄せにする.
        title: const Text('Tabbar'),
        bottom: TabBar(
          onTap: (index) {
            _tabController.animateTo(index);
          },
          controller: _tabController,

書いてあるコードについて調べてみた

with TickerProviderStateMixinとは何か?

TickerMode で定義されるように、現在のツリーが有効な間だけティックするように設定された Ticker オブジェクトを提供します。
このミキシンを使用するクラスで AnimationController を作成するには、新しいアニメーションコントローラを作成するたびに、vsync: this をアニメーションコントローラのコンストラクタに渡します。
Stateの有効期間中、単一のTickerしか持たない場合(例えば、単一のAnimationControllerのみ)、SingleTickerProviderStateMixinを使用する方が効率的である。これは一般的なケースです。
Dartパッケージ

TabControllerとは何か?

/// TabBar]と[TabBarView]が必要とする状態を管理するオブジェクトを作成します。
  /// [TabBarView]の状態を管理するオブジェクトを作成します。
  ///
  /// [length]はNULLや負の値であってはいけません。一般的には、1より大きい値です。
  /// 通常は1より大きい値、つまり通常2つ以上のタブがあります。長さ] は
  /// [TabBar.tabs] と [TabBarView.children] の長さと一致する必要があります。
  ///
  /// `initialIndex` は [length] で指定された有効な値でなければならず、null であってはいけません。もし
  /// [length] が 0 の場合、 `initialIndex` は 0 (デフォルト) でなければなりません。

.animateTo(index)とは何か?
クラスが含まれています。TabController Type: void Function(int, {Curve 曲線, Duration? 持続時間})
indexとpreviousIndexを即座に設定し、現在値からindexまでのアニメーションを再生する。
アニメーションの実行中はindexIsChangingがtrueになります。アニメーションが完了すると、offsetは0.0になります。
Dartパッケージ


今回使用したコード

切り替える画面を作成する。2ページ、3ページとしておきましょうか...

2ページ

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('2ページ',
            style: TextStyle(fontSize: 25, color: Colors.redAccent)),
      ),
    );
  }
}

3ページ

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('3ページ',
            style: TextStyle(fontSize: 25, color: Colors.blueAccent)),
      ),
    );
  }
}

Tabのページ

main.dart
import 'package:counter/second_page.dart';
import 'package:counter/third_page.dart';
import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const TabbarExample(),
    );
  }
}

class TabbarExample extends StatefulWidget {
  const TabbarExample({Key? key}) : super(key: key);

  
  State<TabbarExample> createState() => _TabbarExampleState();
}

class _TabbarExampleState extends State<TabbarExample>
    with TickerProviderStateMixin {
  late TabController _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  
  void dispose() {
    super.dispose();
    _tabController.dispose();
    print('コントローラーの状態を破棄!');
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true, // Androidの場合タイトルを中央寄せにする.
        title: const Text('Tabbar'),
        bottom: TabBar(
          onTap: (index) {// onTapを追加。()の中にindexを書く.
            _tabController.animateTo(index);// animateTo()の中にindexを書く.
          },
          controller: _tabController,
          tabs: const <Widget>[
            Tab(
              icon: Icon(Icons.cloud_outlined),
            ),
            Tab(
              icon: Icon(Icons.beach_access_sharp),
            ),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: const <Widget>[
          // TextWidgetをWedgetClassに変えてページを表示できるようにする.
          SecondePage(), ThirdPage()
        ],
      ),
    );
  }
}

まとめ

私が使ったアプリでは、go_router導入していたのですが、エラーの原因はpackageの影響ではなさそうです???
サンプルアプリは、問題なく動作しているので動作検証はできたと思われるので、一旦問題は解決したと思われます。

Discussion