Анимация подробного сообщения Whatsapp при прокрутке

#flutter #animation #whatsapp

Вопрос:

Я создаю простое приложение для чата с использованием firebase, все хорошо. Но мне любопытно сделать анимацию прокрутки, такую как страница сведений о сообщении WhatsApp. Это анимация, которую я хочу сделать : введите описание изображения здесь

Как вы можете видеть на gif выше, когда я на дату сообщения 23 июля 2021 года затем прокручиваю до даты сообщения 25 июля 2021 года, «Фишка» сверху меняет дату в зависимости от даты сообщения, которую я прокручиваю.

Как я могу это реализовать ? Что я знаю, так это то, что я могу использовать ScrollController, но у меня нет идеи, как это реализовать.

Это мой фиктивный экран :

Примечание : вы должны установить коллекцию пакетов, чтобы запустить мой фиктивный экран

 
Future<void> main() async {
  runApp(MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const WhatsappDetailMessageAnimationScroll(),
      
    );
  }
}

const userIdLogin = '1';

class MessageModel {
  final int id;
  final String messageContent;
  final DateTime messageDate;
  final String senderId;
  final String pairingId;
  MessageModel({
    required this.id,
    required this.messageContent,
    required this.messageDate,
    required this.senderId,
    required this.pairingId,
  });
}

final listMessage = <MessageModel>[
  MessageModel(
    id: 1,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 2,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 3,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 4,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 5,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 6,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 7,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 8,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 9,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 10,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 11,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 12,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 13,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 14,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 15,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 16,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 17,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 18,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 19,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 20,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now(),
    senderId: '1',
    pairingId: '2',
  ),
  MessageModel(
    id: 21,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 22,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 23,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 24,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 25,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 26,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 27,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 28,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 29,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
  MessageModel(
    id: 30,
    messageContent: ' Lorem Ipsum Balalala uhuyyyy',
    messageDate: DateTime.now().subtract(const Duration(days: 1)),
    senderId: '2',
    pairingId: '1',
  ),
]..sort((a, b) => b.messageDate.compareTo(a.messageDate));

/// We group the message depend on date
final groupedByDate = groupBy<MessageModel, DateTime>(listMessage, (item) {
  final date = item.messageDate;
  return DateTime(date.year, date.month, date.day);
});

/// Then we sorting message Descending
final sortMap =
    SplayTreeMap<DateTime, List<MessageModel>>.from(groupedByDate, (a, b) => a.compareTo(b));

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Question SO'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
              children: sortMap.entries.map((map) {
            final key = map.key;
            final values = map.value;
            return Column(
              children: [
                Card(
                  color: colorPallete.monochromaticColor,
                  child: Padding(
                    padding: const EdgeInsets.all(12.0),
                    child: Text(
                      '${key.year}-${key.month}-${key.day}',
                      style: Constant().fontMontserrat.copyWith(color: Colors.white),
                    ),
                  ),
                ),
                ListView.builder(
                  physics: const NeverScrollableScrollPhysics(),
                  reverse: true,
                  shrinkWrap: true,
                  itemCount: values.length,
                  itemBuilder: (context, index) {
                    final value = values[index];
                    return Align(
                      alignment: value.senderId == userIdLogin
                          ? Alignment.centerRight
                          : Alignment.centerLeft,
                      child: Card(
                        child: Padding(
                          padding: const EdgeInsets.all(12.0),
                          child: Text(value.messageContent),
                        ),
                      ),
                    );
                  },
                )
              ],
            );
          }).toList()),
        ),
      ),
    );
  }
}