【问题标题】:Flutter can't keep the state of tabs using PageTransitionSwitcherFlutter 无法使用 PageTransitionSwitcher 保持选项卡的状态
【发布时间】:2021-03-11 00:41:03
【问题描述】:

我正在为 animations 包而苦苦挣扎,我想将动画与 BottomNavigationBar 一起使用。 如果没有动画,我可以使用 IndexedStack 保存我的状态。 如果我将 IndexedStack 包装在 PageTransitionSwitcher 中,它就不起作用。特别是:

  • 动画未显示,但状态保持
  • 如果我使用 IndexedStack 的 key 属性,动画会显示但状态不起作用。

我该如何解决?我不知道如何设置密钥。 非常感谢!!

class MainScreen extends StatefulWidget {
  static String id = 'loading_screen';

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

class _MainScreenState extends State<MainScreen> {
  int _selectedPage = 0;
  List<Widget> pageList = List<Widget>();

  @override
  void initState() {
    pageList.add(PrimoTab());
    pageList.add(SecondoTab());
    pageList.add(TerzoTab());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom tab'),
      ),
      body: PageTransitionSwitcher(
        transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
          return SharedAxisTransition(
            animation: primaryAnimation,
            secondaryAnimation: secondaryAnimation,
            child: child,
            transitionType: SharedAxisTransitionType.horizontal,
          );
        },
        child: IndexedStack(
          index: _selectedPage,
          children: pageList,
          //key: ValueKey<int>(_selectedPage), NOT WORKING
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_car),
            label: 'First Page',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.airplanemode_active),
            label: 'Second Page',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_bike),
            label: 'Third Page',
          ),
        ],
        currentIndex: _selectedPage,
        selectedItemColor: Colors.lightGreen,
        unselectedItemColor: Colors.lightBlueAccent,
        onTap: _onItemTapped,
      ),
    );
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedPage = index;
    });
  }
}

每个选项卡只是一个包含两个文本小部件、一个按钮和一个文本计数器的列(用于测试每个选项卡的状态):

class PrimoTab extends StatefulWidget {

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

class _PrimoTabState extends State<PrimoTab> {
  int cont = -1;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              'TAB 1 - TEXT 1',
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              'TAB 1 - TEXT 2',
            ),
          ),
          FlatButton(
            onPressed: () {
              setState(() {
                cont++;
              });
            },
            child: Text("CLICK"),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              'Valore contatore $cont',
            ),
          ),
        ],
      ),
    );
  }
}

更新 1:仅使用

pageList[_selectedPage],

而不是

IndexedStack(
...
)

但不工作(动画正常,但不保留状态)

使用解决方案更新 2 (main.dart):

void main() {
  runApp(
    MaterialApp(
      home: MainScreen(),
    ),
  );
}

class MainScreen extends StatefulWidget {
  static String id = 'loading_screen';

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

class _MainScreenState extends State<MainScreen> {
  int _selectedPage = 0;
  List<Widget> pageList = List<Widget>();

  @override
  void initState() {
    pageList.add(PrimoTab());
    pageList.add(SecondoTab());
    pageList.add(TerzoTab());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom tab'),
      ),
      body: AnimatedIndexedStack(
        index: _selectedPage,
        children: pageList,
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_car),
            label: 'First Page',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.airplanemode_active),
            label: 'Second Page',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_bike),
            label: 'Third Page',
          ),
        ],
        currentIndex: _selectedPage,
        selectedItemColor: Colors.lightGreen,
        unselectedItemColor: Colors.lightBlueAccent,
        onTap: _onItemTapped,
      ),
    );
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedPage = index;
    });
  }
}

class AnimatedIndexedStack extends StatefulWidget {
  final int index;
  final List<Widget> children;

  const AnimatedIndexedStack({
    Key key,
    this.index,
    this.children,
  }) : super(key: key);

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

class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _animation;
  int _index;

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 150),
    );
    _animation = Tween(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.ease,
      ),
    );

    _index = widget.index;
    _controller.forward();
    super.initState();
  }

  @override
  void didUpdateWidget(AnimatedIndexedStack oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.index != _index) {
      _controller.reverse().then((_) {
        setState(() => _index = widget.index);
        _controller.forward();
      });
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Opacity(
          opacity: _controller.value,
          child: Transform.scale(
            scale: 1.015 - (_controller.value * 0.015),
            child: child,
          ),
        );
      },
      child: IndexedStack(
        index: _index,
        children: widget.children,
      ),
    );
  }
}

【问题讨论】:

    标签: flutter flutter-layout flutter-animation


    【解决方案1】:

    有几种方法可以解决您的问题。它们都不是完全简单的,已经有很多讨论herehere,试图将IndexedStackPageTransitionSwitcher 合并。到目前为止我没有看到任何解决方案。

    我收集了以下可能的方法来实现这一点:

    1. 将状态存储在其他地方并传递给孩子。我还没有看到任何方法可以阻止PageTransitionSwitcher 重建子小部件。 如果您不挖掘子小部件重建,这可能是最直接的方法。

    2. 对动画使用自定义IndexedStack。比如thisthis。与IndexStack中子不会重建的功能配合得很好,但动画效果不如PageTransitionSwitcher,一次只能显示1个widget。

    【讨论】:

    • 不工作 :( 动画没问题,但没有保持状态!
    • 糟糕,乍一看,我一定错过了您的部分问题。然后我更新答案。
    【解决方案2】:

    这是我找到的最接近的解决方案。在保留页面状态的同时获得很少的动画

    class AnimatedIndexedStack extends StatefulWidget {
      final int index;
      final List<Widget> children;
    
      const AnimatedIndexedStack({
        Key key,
        this.index,
        this.children,
      }) : super(key: key);
    
      @override
      _AnimatedIndexedStackState createState() => _AnimatedIndexedStackState();
    }
    
    class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
        with SingleTickerProviderStateMixin {
      AnimationController _controller;
      Animation<double> _animation;
      int _index;
    
      @override
      void initState() {
        _controller = AnimationController(
          vsync: this,
          duration: Duration(milliseconds: 150),
        );
        _animation = Tween(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: _controller,
            curve: Curves.ease,
          ),
        );
    
        _index = widget.index;
        _controller.forward();
        super.initState();
      }
    
      @override
      void didUpdateWidget(AnimatedIndexedStack oldWidget) {
        super.didUpdateWidget(oldWidget);
        if (widget.index != _index) {
          _controller.reverse().then((_) {
            setState(() => _index = widget.index);
            _controller.forward();
          });
        }
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return Opacity(
              opacity: _controller.value,
              child: Transform.scale(
                scale: 1.015 - (_controller.value * 0.015),
                child: child,
              ),
            );
          },
          child: IndexedStack(
            index: _index,
            children: widget.children,
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 2018-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多