Home > Software design >  In Flutter, what's the easiest, simplest way to make a container flashing once without involvin
In Flutter, what's the easiest, simplest way to make a container flashing once without involvin

Time:03-29

Imagine Facebook mobile app, where you tap on the notification about someone like your comment. The app will open the appropriate screen, scroll you down to the comment, and after you arrive there, the comment row will flash yellow for a while, rapidly turn transparent, and then it's done.

I just want to make the same flashing animation to a ListView/Column element to let users know that something is happening there as a result of their action. But from what I gathered, to create just a simple animation like that needs a complex elaborate contraption with Animation widgets.

There's a widget that does a much appreciated fade animation called FadeInImage. I just need to provide destination URL, placeholder image asset, and the widget will handle the rest. I'm wondering if there's such alternative where I can just provide a key to a widget, and then call from anywhere: rowKey.currentState.flash(color: Colors.yellow). Or perhaps a way to let me tell the ListView or Column to flash certain row like listViewKey.currentState.items[5].flash(color: Colors.yellow).

CodePudding user response:

There is no a Widget like you are looking for, but you can create a custom widget if you know the Flutter basics. You will be able to build from simple animations to the most advanced ones.

I made a simple example, a list of elements where you can select any element from the list (index).

When you open the screen, you will see the scroll animation, after that, the blink animation will start.

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

  void _goToWidget(BuildContext context, int index) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => FlashingList(index: index),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            MaterialButton(
              color: Colors.greenAccent,
              child: const Text('Go to element 5'),
              onPressed: () => _goToWidget(context, 5),
            ),
            MaterialButton(
              color: Colors.greenAccent,
              child: const Text('Go to element 10'),
              onPressed: () => _goToWidget(context, 10),
            ),
          ],
        ),
      ),
    );
  }
}

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

  final int index;

  @override
  State<FlashingList> createState() => _FlashingListState();
}

class _FlashingListState extends State<FlashingList>
    with SingleTickerProviderStateMixin {
  final _scrollController = ScrollController();
  late final AnimationController _animationController;
  final _itemSize = 150.0;
  Timer? _timer;

  Future<void> _startScrolling() async {
    await _scrollController.animateTo(
      _itemSize * widget.index,
      duration: const Duration(seconds: 1),
      curve: Curves.easeOut,
    );
    // after the scroll animation finishes, start the blinking
    _animationController.repeat(reverse: true);
    // the duration of the blinking
    _timer = Timer(const Duration(seconds: 3), () {
      setState(() {
        _animationController.stop();
        _timer?.cancel();
      });
    });
  }

  @override
  void initState() {
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );
    WidgetsBinding.instance!.addPostFrameCallback((_) => _startScrolling());
    super.initState();
  }

  @override
  void dispose() {
    _timer?.cancel();
    _animationController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flashing List'),
      ),
      body: ListView.builder(
        controller: _scrollController,
        itemCount: 15,
        itemExtent: 150,
        itemBuilder: (context, index) {
          final item = Padding(
            padding: const EdgeInsets.all(20.0),
            child: Text('My Item :$index'),
          );
          return Padding(
            padding: const EdgeInsets.all(4.0),
            child: FittedBox(
              child: index == widget.index && _animationController.isDismissed
                  ? FadeTransition(
                      opacity: _animationController,
                      child: Container(
                        color: Colors.yellow,
                        child: item,
                      ),
                    )
                  : Container(
                      color: Colors.grey[200],
                      child: item,
                    ),
            ),
          );
        },
      ),
    );
  }
}

Result:

enter image description here

Now that you know how to create an automatic scrolling list, animated item, you can customize this with a more complex animation and extract into a custom widget that you can reuse in your projects.

Reference: enter image description hereenter image description here

  • Related