skip to Main Content

As it can be seen in the GIF below that whenever the child exits the parent Container the child disappears without animation, which creates a bad impression on the user. How to add a smooth transition exit for the entering and exiting child ?

Chat GPT gave an answer which seems to have the right logic but it gives the same abrupt effect.

Chat GPT Code : –

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Smooth Transition in ListWheelScrollView')),
        body: TransitionListWheel(),
      ),
    );
  }
}

class TransitionListWheel extends StatefulWidget {
  @override
  _TransitionListWheelState createState() => _TransitionListWheelState();
}

class _TransitionListWheelState extends State<TransitionListWheel> {
  FixedExtentScrollController _scrollController;
  double itemHeight = 100.0; // Height of each item in the list
  int itemCount = 20;

  @override
  void initState() {
    super.initState();
    _scrollController = FixedExtentScrollController();
    _scrollController.addListener(_handleScroll);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_handleScroll);
    _scrollController.dispose();
    super.dispose();
  }

  void _handleScroll() {
    setState(() {
      // Trigger a rebuild to update the transition animations
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: itemHeight * 5, // Visible height of the list
        child: ListWheelScrollView.useDelegate(
          controller: _scrollController,
          itemExtent: itemHeight,
          physics: FixedExtentScrollPhysics(),
          childDelegate: ListWheelChildBuilderDelegate(
            builder: (context, index) {
              final double scrollOffset = _scrollController.offset;
              final double itemScrollOffset = scrollOffset % itemHeight;

              // Calculate transition values for entering and exiting animations
              final double enteringScale = 1.0 - (itemScrollOffset / itemHeight);
              final double exitingScale = itemScrollOffset / itemHeight;

              return AnimatedBuilder(
                animation: _scrollController,
                builder: (context, child) {
                  return Transform.scale(
                    scale: (enteringScale + exitingScale).clamp(0.6, 1.0), // Adjust the range as needed
                    child: child,
                  );
                },
                child: Center(
                  child: Container(
                    width: 200,
                    height: 80,
                    color: Colors.blue,
                    child: Center(
                      child: Text(
                        'Item $index',
                        style: TextStyle(color: Colors.white),
                      ),
                    ),
                  ),
                ),
              );
            },
            childCount: itemCount,
          ),
        ),
      ),
    );
  }
}

Code which I use : –

import 'package:flutter/material.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: 'List',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const List(),
    );
  }
}

class List extends StatefulWidget {
  const List({Key? key}) : super(key: key);
  @override
  _ListState createState() => _ListState();
}

class _ListState extends State<List> {
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
       
        body: Center(
          child: SizedBox(
            height: 500,
            child: ListWheelScrollView(
                itemExtent: 100,
                physics: const FixedExtentScrollPhysics(),
                onSelectedItemChanged: (value) {
                  
                },
                children: [
                  for (int i = 0; i < 5; i++) ...[
                    Container(
                      color: Colors.green,
                      height: 50,
                      width: 50,
                    )
                  ]
                ]),
          ),
        ));
  }
}

2

Answers


  1. I think this package will solve your problem. https://pub.dev/packages/clickable_list_wheel_view

    Example:

    import 'package:clickable_list_wheel_view/clickable_list_wheel_widget.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Material(
            child: MyHomePage(),
          ),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      final _scrollController = FixedExtentScrollController();
    
      static const double _itemHeight = 60;
      static const int _itemCount = 100;
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            body: ClickableListWheelScrollView(
              scrollController: _scrollController,
              itemHeight: _itemHeight,
              itemCount: _itemCount,
              onItemTapCallback: (index) {
                print("onItemTapCallback index: $index");
              },
              child: ListWheelScrollView.useDelegate(
                controller: _scrollController,
                itemExtent: _itemHeight,
                physics: FixedExtentScrollPhysics(),
                overAndUnderCenterOpacity: 0.5,
                perspective: 0.002,
                onSelectedItemChanged: (index) {
                  print("onSelectedItemChanged index: $index");
                },
                childDelegate: ListWheelChildBuilderDelegate(
                  builder: (context, index) => _child(index),
                  childCount: _itemCount,
                ),
              ),
            ),
          ),
        );
      }
    
      Widget _child(int index) {
        return SizedBox(
          height: _itemHeight,
          child: ListTile(
            leading: Icon(
                IconData(int.parse("0xe${index + 200}"),
                    fontFamily: 'MaterialIcons'),
                size: 50),
            title: Text('Heart Shaker'),
            subtitle: Text('Description here'),
          ),
        );
      }
    }
    

    I couldn’t post it here because the gif size is high. You can see the output of this code from the link I shared: https://raw.githubusercontent.com/cilestal/clickable_list_wheel_view/master/example/example.gif

    I hope I have helped. Enjoy your work.

    Login or Signup to reply.
  2. I extended the ChatGPT code, try it:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: Text('Smooth Transition in ListWheelScrollView')),
            body: TransitionListWheel(),
          ),
        );
      }
    }
    
    class TransitionListWheel extends StatefulWidget {
      @override
      _TransitionListWheelState createState() => _TransitionListWheelState();
    }
    
    class _TransitionListWheelState extends State<TransitionListWheel> {
      late final FixedExtentScrollController _scrollController;
      double itemHeight = 100.0; // Height of each item in the list
      int itemCount = 20;
      int viewCount = 5;
    
      @override
      void initState() {
        super.initState();
        _scrollController = FixedExtentScrollController();
        _scrollController.addListener(_handleScroll);
      }
    
      @override
      void dispose() {
        _scrollController.removeListener(_handleScroll);
        _scrollController.dispose();
        super.dispose();
      }
    
      void _handleScroll() {
        setState(() {
          // Trigger a rebuild to update the transition animations
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Container(
            height: itemHeight * viewCount, // Visible height of the list
            child: ListWheelScrollView.useDelegate(
              controller: _scrollController,
              itemExtent: itemHeight,
              physics: FixedExtentScrollPhysics(),
              childDelegate: ListWheelChildBuilderDelegate(
                builder: (context, index) {
                  final double scrollOffset = _scrollController.offset;
                  final double itemScrollOffset = scrollOffset % itemHeight;
    
                  // Calculate transition values for entering and exiting animations
                  final double enteringScale =
                      1.0 - (itemScrollOffset / itemHeight);
                  final double exitingScale = itemScrollOffset / itemHeight;
    
                  final centerOffset = index * itemHeight - scrollOffset;
                  final itemOpacity = (double x) {
                    if (x < viewCount ~/ 2 * itemHeight) return 1.0;
                    if (x > (viewCount ~/ 2 + 1) * itemHeight) return 0.0;
                    return 1 - (x - viewCount ~/ 2 * itemHeight) / 100;
                  }(centerOffset.abs() + 25); // adjust as needed
    
                  return AnimatedBuilder(
                    animation: _scrollController,
                    builder: (context, child) {
                      //return Text('$opacity');
                      return Transform.scale(
                        scale: (enteringScale + exitingScale)
                            .clamp(0.6, 1.0), // Adjust the range as needed
                        child: Opacity(opacity: itemOpacity, child: child),
                      );
                    },
                    child: Center(
                      child: Container(
                        width: 200,
                        height: 80,
                        color: Colors.blue,
                        child: Center(
                          child: Text(
                            'Item $index',
                            style: TextStyle(color: Colors.white),
                          ),
                        ),
                      ),
                    ),
                  );
                },
                childCount: itemCount,
              ),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search