Flutter는 PageTransitionSwitcher를 사용하여 탭 상태를 유지할 수 없습니다.

Nov 28 2020

애니메이션 패키지로 어려움을 겪고 있으며 BottomNavigationBar에서 애니메이션을 사용하고 싶습니다. 애니메이션없이 IndexedStack을 사용하여 상태를 저장할 수 있습니다. PageTransitionSwitcher 안에 IndexedStack을 래핑하면 작동하지 않습니다. 특히:

  • 애니메이션이 표시되지 않지만 상태는 유지됩니다.
  • IndexedStack의 키 속성을 사용하면 애니메이션이 표시되지만 상태가 작동하지 않습니다.

어떻게 고칠 수 있습니까? 키 설정 방법을 모르겠습니다. 대단히 감사합니다 !!

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,
      ),
    );
  }
}

답변

1 yellowgray Nov 29 2020 at 06:27

상황을 해결하는 방법은 거의 없습니다. 그들 중 누구도 이미 많은 토론이 완벽하게 간단하지 않으며 여기 와 여기에 병합하려고 IndexedStack으로 PageTransitionSwitcher. 지금까지 내가 본 해결책이 없습니다.

이를 위해 다음과 같은 가능한 방법을 수집합니다.

  1. 상태를 다른 곳에 저장하고 자녀에게 전달하십시오. 나는 어떤 방법도 PageTransitionSwitcher자식 위젯을 다시 만드는 것을 멈출 수있는 것을 보지 못했습니다 . 만약 당신이 자식 위젯 rebuild를 채굴하지 않는다면 , 이것이 가장 간단한 방법 일 수 있습니다.

  2. IndexedStack애니메이션과 함께 사용자 정의 를 사용하십시오 . 이것 과 이것 처럼 . 아이들이 다시 빌드하지 않는 IndexStack의 기능과 잘 작동하지만 애니메이션만큼 좋지 않으며 PageTransitionSwitcher한 번에 하나의 위젯 만 표시 할 수 있습니다.