import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:retry/retry.dart'; import 'package:intl/intl.dart'; import 'package:navbar_router/navbar_router.dart'; import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:cached_network_image/cached_network_image.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); final List colors = [mediumPurple, Colors.orange, Colors.teal]; @override Widget build(BuildContext context) { return MaterialApp( title: 'BottomNavbar Demo', theme: ThemeData( primarySwatch: Colors.indigo, ), home: HomePage()); // home: const NavbarSample(title: 'BottomNavbar Demo')); } } class HomePage extends StatelessWidget { HomePage({Key? key}) : super(key: key); List items = [ NavbarItem( Icons.home, 'Home', ), NavbarItem( Icons.notifications, 'Notifications', ), NavbarItem(Icons.explore, 'Explore'), NavbarItem(Icons.person, 'Profile'), ]; final Map> _routes = const { 0: { '/': HomeScreen(), }, 1: { '/': NotificationPage(), }, 2: { '/': MyEvents(), }, 3: { '/': HomeScreen(), }, }; @override Widget build(BuildContext context) { return NavbarRouter( errorBuilder: (context) { return const Center(child: Text('Error 404')); }, onBackButtonPressed: (isExiting) { return isExiting; }, destinationAnimationCurve: Curves.fastOutSlowIn, destinationAnimationDuration: 600, decoration: NavbarDecoration( backgroundColor: Colors.white, selectedIconTheme: const IconThemeData(color: Colors.black), navbarType: BottomNavigationBarType.fixed, elevation: 18, selectedLabelTextStyle: const TextStyle(color: Colors.black), enableFeedback: true), destinations: [ for (int i = 0; i < items.length; i++) DestinationRouter( navbarItem: items[i], destinations: [ for (int j = 0; j < _routes[i]!.keys.length; j++) Destination( route: _routes[i]!.keys.elementAt(j), widget: _routes[i]!.values.elementAt(j), ), ], initialRoute: _routes[i]!.keys.first, ), ], ); } } class HomeScreen extends StatelessWidget { static var route; const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, automaticallyImplyLeading: false, toolbarHeight: MediaQuery.of(context).size.height * 0.096, elevation: 0.3, title: Transform( transform: Matrix4.translationValues(8.0, -5.6, 0), child: const Text( "Home", textAlign: TextAlign.left, textScaleFactor: 1.3, style: TextStyle(color: Color.fromARGB(255, 30, 29, 29)), ), )), ); } } class NotificationPage extends StatefulWidget { const NotificationPage({super.key}); @override State createState() => _NotificationPageState(); } class _NotificationPageState extends State { bool showFrom = false; //set variable for Connectivity subscription listiner final url = "https://gist.githubusercontent.com/tushar4303/0ababbdad3073acd8ab2580b5deb084b/raw/e56d65b028681c9beb3074657e9d81f0d33f5391/notifications.json"; final _filters = []; final _senders = []; final List _filteredNotifications = []; late Future?> myfuture; @override void initState() { // using this listiner, you can get the medium of connection as well. super.initState(); myfuture = loadNotifications(); } @override void dispose() { super.dispose(); } Future?> loadNotifications() async { final r = RetryOptions(maxAttempts: 3); final response = await r.retry( // Make a GET request () => http.get(Uri.parse(url)).timeout(Duration(seconds: 2)), // Retry on SocketException or TimeoutException retryIf: (e) => e is SocketException || e is TimeoutException, ); try { if (response.statusCode == 200) { final NotificationsJson = response.body; final decodedData = jsonDecode(NotificationsJson); var notificationsData = decodedData["notifications"]; var labelsData = decodedData["labels"]; var senderRolesData = decodedData["senderRoles"]; NotificationModel.labels = List.from(labelsData); NotificationModel.senderRoles = List.from(senderRolesData); NotificationModel.items = List.from(notificationsData) .map((item) => Item.fromMap(item)) .toList(); setState(() { _filters.clear(); _senders.clear(); showFrom = false; _filteredNotifications.clear(); _filteredNotifications.addAll(NotificationModel.items!); }); return NotificationModel.items; } } catch (e) { throw Exception(e.toString()); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromARGB(255, 255, 255, 255), appBar: AppBar( backgroundColor: Colors.transparent, automaticallyImplyLeading: false, toolbarHeight: MediaQuery.of(context).size.height * 0.125, elevation: 0.3, // title: Text("Notifications"), title: Transform( transform: Matrix4.translationValues(8.0, 16.0, 0), child: Column( // mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Notifications", textAlign: TextAlign.left, textScaleFactor: 1.3, style: TextStyle(color: Color.fromARGB(255, 30, 29, 29)), ), SizedBox( height: 4, ), (NotificationModel.labels != null && NotificationModel.labels!.isNotEmpty) ? SingleChildScrollView( scrollDirection: Axis.horizontal, child: Container( margin: EdgeInsets.only(bottom: 16), child: Row( children: [ Transform( transform: Matrix4.translationValues(0, -4, 0), child: Visibility( visible: showFrom, child: Padding( padding: const EdgeInsets.only(right: 16), child: ActionChip( onPressed: () { //if filtertype == Academics then call show modalbottomsheet showModalBottomSheet( enableDrag: true, useRootNavigator: true, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(25.0), ), ), context: context, builder: (builder) { return SingleChildScrollView( child: SizedBox( child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: const Radius .circular(20.0), topRight: const Radius .circular(20.0))), //content starts child: Container( margin: EdgeInsets.only( right: 5.0, left: 5.0, top: 10.0), child: Column( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ // rounded rectangle grey handle Container( width: 40.0, height: 5.0, decoration: BoxDecoration( borderRadius: BorderRadius .circular( 10.0), color: Colors.grey, ), ), SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment .start, mainAxisAlignment: MainAxisAlignment .start, children: (NotificationModel .senderRoles! .map( (sender) { return Column( children: [ ListTile( title: Text( sender), trailing: Visibility( visible: _senders.contains(sender), child: Icon( Icons .done, color: Colors.blueAccent, ), ), onTap: () { setState( () { if (_senders .contains(sender)) { _senders.remove(sender); } else { _senders.add(sender); } }); }, ), Divider(), ], ); }).toList())), ), ], ), ), ), ), ); }); }, label: Text("From"), avatar: Icon( Icons.account_circle, color: Colors.black, ), visualDensity: VisualDensity(vertical: -1.5), side: BorderSide( width: 1, color: Color.fromARGB(66, 75, 74, 74)), // surfaceTintColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), ), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.start, children: (NotificationModel.labels!.map((filterType) { return Transform( transform: Matrix4.identity()..scale(0.85), child: FilterChip( checkmarkColor: Colors.black, label: Text( filterType, textScaleFactor: 1.1, ), // labelPadding: EdgeInsets.symmetric( // horizontal: 4, vertical: 0), selected: _filters.contains(filterType), side: BorderSide( width: 1, color: Color.fromARGB(66, 75, 74, 74)), // surfaceTintColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), selectedColor: Color.fromARGB(180, 224, 220, 220), onSelected: ((value) { setState(() { if (value) { _filters.add(filterType); } else { _filters.removeWhere((name) { return name == filterType; }); } _filteredNotifications.clear(); if (_filters .contains("Academics")) { setState(() { showFrom = true; }); } else { setState(() { showFrom = false; }); } if (_filters.isEmpty) { _filteredNotifications.addAll( NotificationModel.items!); } else { _filteredNotifications.addAll( NotificationModel.items!.where( (notification) => _filters .contains(notification .messageLabel))); } }); }))); }).toList()), ), ], ), ), ) : Center( child: CircularProgressIndicator( color: Colors.white, )), ], ), ), ), body: Column( children: [ Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: FutureBuilder( future: myfuture, builder: ((context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { return RefreshIndicator( onRefresh: () async { loadNotifications(); }, child: Padding( padding: const EdgeInsets.only(bottom: 54), child: ListView.builder( itemCount: _filteredNotifications.length, itemBuilder: (context, index) { return NotificationWidget( item: _filteredNotifications[index], ); }), ), ); } else if (snapshot.hasError) { return Text("Some error has occured"); } } return Center( child: CircularProgressIndicator( color: Colors.black, ), ); }))), ), ], ), // ignore: prefer_const_literals_to_create_immutables ); } } class MyEvents extends StatefulWidget { const MyEvents({super.key}); @override State createState() => _MyEventsState(); } class _MyEventsState extends State { bool showFrom = false; //set variable for Connectivity subscription listiner final url = "https://gist.githubusercontent.com/tushar4303/675432e0e112e258c971986dbca37156/raw/d805186a9b7fb95b8606fe2b2257fcadadb2a47c/events.json"; final _filters = []; final _senders = []; final List _filteredEvents = []; late Future?> myfuture; @override void initState() { // using this listiner, you can get the medium of connection as well. super.initState(); myfuture = loadEvents(); } @override void dispose() { super.dispose(); } Future?> loadEvents() async { final r = RetryOptions(maxAttempts: 3); final response = await r.retry( // Make a GET request () => http.get(Uri.parse(url)).timeout(Duration(seconds: 2)), // Retry on SocketException or TimeoutException retryIf: (e) => e is SocketException || e is TimeoutException, ); try { if (response.statusCode == 200) { final EventsJson = response.body; final decodedData = jsonDecode(EventsJson); var eventsData = decodedData["events"]; var labelsData = decodedData["labels"]; var senderRolesData = decodedData["senderRoles"]; NotificationModel.labels = List.from(labelsData); NotificationModel.senderRoles = List.from(senderRolesData); NotificationModel.items = List.from(eventsData) .map((item) => Item.fromMap(item)) .toList(); setState(() { _filters.clear(); _senders.clear(); showFrom = false; _filteredEvents.clear(); _filteredEvents.addAll(NotificationModel.items!); }); return NotificationModel.items; } } catch (e) { throw Exception(e.toString()); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromARGB(255, 255, 255, 255), appBar: AppBar( backgroundColor: Colors.transparent, automaticallyImplyLeading: false, toolbarHeight: MediaQuery.of(context).size.height * 0.125, elevation: 0.3, // title: Text("Notifications"), title: Transform( transform: Matrix4.translationValues(8.0, 16.0, 0), child: Column( // mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Events", textAlign: TextAlign.left, textScaleFactor: 1.3, style: TextStyle(color: Color.fromARGB(255, 30, 29, 29)), ), SizedBox( height: 4, ), (NotificationModel.labels != null && NotificationModel.labels!.isNotEmpty) ? SingleChildScrollView( scrollDirection: Axis.horizontal, child: Container( margin: EdgeInsets.only(bottom: 16), child: Row( children: [ Transform( transform: Matrix4.translationValues(0, -4, 0), child: Visibility( visible: showFrom, child: Padding( padding: const EdgeInsets.only(right: 16), child: ActionChip( onPressed: () { //if filtertype == Academics then call show modalbottomsheet showModalBottomSheet( enableDrag: true, useRootNavigator: true, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(25.0), ), ), context: context, builder: (builder) { return SingleChildScrollView( child: SizedBox( child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: const Radius .circular(20.0), topRight: const Radius .circular(20.0))), //content starts child: Container( margin: EdgeInsets.only( right: 5.0, left: 5.0, top: 10.0), child: Column( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ // rounded rectangle grey handle Container( width: 40.0, height: 5.0, decoration: BoxDecoration( borderRadius: BorderRadius .circular( 10.0), color: Colors.grey, ), ), SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment .start, mainAxisAlignment: MainAxisAlignment .start, children: (NotificationModel .senderRoles! .map( (sender) { return Column( children: [ ListTile( title: Text( sender), trailing: Visibility( visible: _senders.contains(sender), child: Icon( Icons .done, color: Colors.blueAccent, ), ), onTap: () { setState( () { if (_senders .contains(sender)) { _senders.remove(sender); } else { _senders.add(sender); } }); }, ), Divider(), ], ); }).toList())), ), ], ), ), ), ), ); }); }, label: Text("From"), avatar: Icon( Icons.auto_awesome, color: Colors.black, ), visualDensity: VisualDensity(vertical: -1.5), side: BorderSide( width: 1, color: Color.fromARGB(66, 75, 74, 74)), // surfaceTintColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), ), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.start, children: (NotificationModel.labels!.map((filterType) { return Transform( transform: Matrix4.identity()..scale(0.85), child: FilterChip( checkmarkColor: Colors.black, label: Text( filterType, textScaleFactor: 1.1, ), // labelPadding: EdgeInsets.symmetric( // horizontal: 4, vertical: 0), selected: _filters.contains(filterType), side: BorderSide( width: 1, color: Color.fromARGB(66, 75, 74, 74)), // surfaceTintColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), selectedColor: Color.fromARGB(180, 224, 220, 220), onSelected: ((value) { setState(() { if (value) { _filters.add(filterType); } else { _filters.removeWhere((name) { return name == filterType; }); } _filteredEvents.clear(); if (_filters .contains("Technical")) { setState(() { showFrom = true; }); } else { setState(() { showFrom = false; }); } if (_filters.isEmpty) { _filteredEvents.addAll( NotificationModel.items!); } else { _filteredEvents.addAll( NotificationModel.items!.where( (notification) => _filters .contains(notification .messageLabel))); } }); }))); }).toList()), ), ], ), ), ) : Center( child: CircularProgressIndicator( color: Colors.white, )), ], ), ), ), body: Column( children: [ Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: FutureBuilder( future: myfuture, builder: ((context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { return RefreshIndicator( onRefresh: () async { loadEvents(); }, child: Padding( padding: const EdgeInsets.only(bottom: 54, top: 4), child: GridView.builder( itemCount: _filteredEvents.length, itemBuilder: (context, index) { return EventsWidget( item: _filteredEvents[index]); }, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 16.0, mainAxisSpacing: 12.0, mainAxisExtent: 310.0), ), ), ); } else if (snapshot.hasError) { return Text("Some error has occured"); } } return Center( child: CircularProgressIndicator( color: Colors.black, ), ); }))), ), ], ), // ignore: prefer_const_literals_to_create_immutables ); } } class EventsWidget extends StatelessWidget { const EventsWidget({super.key, required this.item}); final Item item; @override Widget build(BuildContext context) { return Hero( tag: Key(item.messageId.toString()), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => NotificationDetailsPage(item: item))); }, child: Column( children: [ ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(12)), child: Stack( children: [ Container( height: 260, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12.0)), child: CachedNetworkImage( fit: BoxFit.cover, imageUrl: item.imageUrl, placeholder: (context, url) { return Image.asset( "assets/images/placeholder.png", fit: BoxFit.cover, ); }, ), ), Positioned( child: Container( margin: const EdgeInsets.only(top: 236), height: 24, color: const Color.fromARGB(165, 0, 0, 0), child: Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(left: 8), child: RichText( maxLines: 2, textAlign: TextAlign.left, overflow: TextOverflow.ellipsis, text: TextSpan( text: null, style: const TextStyle( fontSize: 12, color: Colors.black87), children: [ const WidgetSpan( child: Icon( Icons.watch_later_outlined, size: 12, color: Colors.white, ), ), TextSpan( text: " ${DateFormat('MMM d, ' 'yy').format(item.dateOfcreation)}", style: const TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.w500)), ]), ), ), ), ), ), ], ), ), const SizedBox( height: 8, ), Align( alignment: Alignment.topLeft, child: Text(item.messageTitle, maxLines: 2, textAlign: TextAlign.left, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ), // Align( // alignment: Alignment.topLeft, // child: Text( // item // .messageLabel, // maxLines: 2, // textAlign: TextAlign.left, // overflow: TextOverflow.ellipsis, // style: const TextStyle( // fontSize: 14, // )), // ), ], ), )); } } // ignore_for_file: prefer_const_constructors // ignore_for_file: public_member_api_docs, sort_constructors_first class NotificationModel { static List? labels; static List? senderRoles; static List? items; } class SenderRoles { final List senderRoles; SenderRoles( this.senderRoles, ); SenderRoles copyWith({ List? senderRoles, }) { return SenderRoles( senderRoles ?? this.senderRoles, ); } Map toMap() { return { 'senderRoles': senderRoles, }; } factory SenderRoles.fromMap(Map map) { return SenderRoles(List.from( (map['senderRoles'] as List), )); } String toJson() => json.encode(toMap()); factory SenderRoles.fromJson(String source) => SenderRoles.fromMap(json.decode(source) as Map); @override String toString() => 'SenderRoles(senderRoles: $senderRoles)'; @override bool operator ==(covariant SenderRoles other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; return listEquals(other.senderRoles, senderRoles); } @override int get hashCode => senderRoles.hashCode; } class Labels { final List labels; Labels( this.labels, ); Labels copyWith({ List? labels, }) { return Labels( labels ?? this.labels, ); } Map toMap() { return { 'labels': labels, }; } factory Labels.fromMap(Map map) { return Labels(List.from( (map['labels'] as List), )); } String toJson() => json.encode(toMap()); factory Labels.fromJson(String source) => Labels.fromMap(json.decode(source) as Map); @override String toString() => 'Labels(labels: $labels)'; @override bool operator ==(covariant Labels other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; return listEquals(other.labels, labels); } @override int get hashCode => labels.hashCode; } class Item { final int messageId; final String userName; final String userRole; final String messageTitle; final String messageLabel; final String userMessage; final String imageUrl; final DateTime dateOfcreation; Item( {required this.messageId, required this.userName, required this.userRole, required this.messageTitle, required this.messageLabel, required this.userMessage, required this.imageUrl, required this.dateOfcreation}); factory Item.fromMap(Map map) { return Item( messageId: map["message_id"], userName: map["userName"], userRole: map["userRole"], messageTitle: map["message_title"], messageLabel: map["message_label"], userMessage: map["user_message"], imageUrl: map["image_url"], dateOfcreation: DateTime.parse(map["dateOfcreation"]), ); } toMap() => { "message_id": messageId, "userName": userName, "userRole": userRole, "message_title": messageTitle, "message_label": messageLabel, "user_message": userMessage, "image_url": imageUrl, "dateOfcreation": dateOfcreation, }; @override String toString() { return 'Item(messageId: $messageId, userName: $userName, userRole: $userRole, messageTitle: $messageTitle, messageLabel: $messageLabel, userMessage: $userMessage, imageUrl: $imageUrl, dateOfcreation: $dateOfcreation)'; } } class NotificationWidget extends StatelessWidget { const NotificationWidget({super.key, required this.item}); final Item item; @override Widget build(BuildContext context) { return Hero( tag: Key(item.messageId.toString()), child: SizedBox( width: double.infinity, // height: MediaQuery.of(context).size.height * 0.1155, child: Card( // color: Colors.amberAccent, elevation: 0.0, margin: const EdgeInsets.only(bottom: 12), child: ListTile( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => NotificationDetailsPage(item: item))); }, leading: CircleAvatar( backgroundImage: NetworkImage(item.imageUrl), child: const Text('DP')), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( maxLines: 1, overflow: TextOverflow.ellipsis, text: TextSpan( text: item.userRole, style: const TextStyle( fontWeight: FontWeight.w600, color: Colors.black), children: [ TextSpan( text: " @${item.userName}", style: const TextStyle(fontWeight: FontWeight.w400), ) ], ), ), const SizedBox( height: 1, ), Text(item.messageTitle, textScaleFactor: 0.9, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w600)), Padding( padding: const EdgeInsets.only(top: 3), child: Text( item.userMessage, textScaleFactor: 0.9, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), trailing: Text( DateFormat('MMM d, ' 'yy').format(item.dateOfcreation), textAlign: TextAlign.end, textScaleFactor: 0.8, ), ), ), ), ); } } // ignore_for_file: prefer_const_constructors class NotificationDetailsPage extends StatelessWidget { const NotificationDetailsPage({ Key? key, required this.item, }) : super(key: key); final Item item; @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( backgroundColor: Colors.white, // appBar: SliverAppBar(), body: NestedScrollView( floatHeaderSlivers: true, headerSliverBuilder: (context, innerBoxIsScrolled) => [ const SliverAppBar( floating: true, ) ], body: Padding( padding: const EdgeInsets.only( left: 24, right: 24, bottom: 54, top: 8), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.only(top: 8, bottom: 16), decoration: const BoxDecoration( color: Color.fromRGBO(240, 221, 245, 1), borderRadius: BorderRadius.all(Radius.circular(10))), child: Padding( padding: const EdgeInsets.all(8.0), child: Text( item.messageLabel, style: const TextStyle( fontWeight: FontWeight.w500, color: Color.fromRGBO(30, 29, 29, 0.8)), ), ), ), Text( item.messageTitle, style: const TextStyle( fontWeight: FontWeight.w400, fontSize: 28, color: Color.fromARGB(255, 30, 29, 29)), ), //start Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ CircleAvatar( backgroundImage: NetworkImage(item.imageUrl)), const SizedBox( width: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan( text: item.userRole, style: const TextStyle( fontWeight: FontWeight.w600, color: Colors.black), children: [ TextSpan( text: " @${item.userName}", style: const TextStyle( fontWeight: FontWeight.w400), ) ], ), ), Row( children: [ const Icon( Icons.watch_later_outlined, size: 16, ), const SizedBox( width: 2, ), Text(timeago.format(item.dateOfcreation)), ], ), ], ) ], ), ), Hero( tag: Key(item.messageId.toString()), child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: CachedNetworkImage( imageUrl: item.imageUrl, placeholder: (context, url) { return Image.asset( "assets/images/placeholder.png", fit: BoxFit.cover, ); }, ), ), ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Text( item.userMessage, textAlign: TextAlign.left, style: const TextStyle(fontSize: 16), ), ) ], ), ), ))), ); } }