Created
October 15, 2024 13:58
-
-
Save callmephil/aaffeb12183cd85ffd5ffc6058475c6a to your computer and use it in GitHub Desktop.
(Flutter) Firestore stream example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AlertModel { | |
final String id; | |
final String title; | |
final String message; | |
final DateTime createdAt; | |
const AlertModel({ | |
required this.id, | |
required this.title, | |
required this.message, | |
required this.createdAt, | |
}); | |
String get timeAgo { | |
final now = DateTime.now(); | |
final diff = now.difference(createdAt); | |
if (diff.inDays > 365) { | |
return '${diff.inDays ~/ 365}y'; | |
} else if (diff.inDays > 30) { | |
return '${diff.inDays ~/ 30}mo'; | |
} else if (diff.inDays > 7) { | |
return '${diff.inDays ~/ 7}w'; | |
} else if (diff.inDays > 0) { | |
return '${diff.inDays}d'; | |
} else if (diff.inHours > 0) { | |
return '${diff.inHours}h'; | |
} else if (diff.inMinutes > 0) { | |
return '${diff.inMinutes}m'; | |
} | |
return 'Just Now'; | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'id': id, | |
'title': title, | |
'message': message, | |
'createdAt': createdAt.toIso8601String(), | |
}; | |
} | |
factory AlertModel.fromJson(Map<String, dynamic> json) { | |
return AlertModel( | |
id: json['id'], | |
title: json['title'], | |
message: json['message'], | |
createdAt: DateTime.parse(json['createdAt']), | |
); | |
} | |
AlertModel copyWith({ | |
String? title, | |
String? message, | |
DateTime? createdAt, | |
}) { | |
return AlertModel( | |
id: id, | |
title: title ?? this.title, | |
message: message ?? this.message, | |
createdAt: createdAt ?? this.createdAt, | |
); | |
} | |
@override | |
bool operator ==(Object other) { | |
if (identical(this, other)) return true; | |
return other is AlertModel && | |
other.id == id && | |
other.title == title && | |
other.message == message && | |
other.createdAt == createdAt; | |
} | |
@override | |
int get hashCode { | |
return id.hashCode ^ title.hashCode ^ message.hashCode ^ createdAt.hashCode; | |
} | |
@override | |
String toString() => | |
'AlertsModel(title: $title, message: $message, createdAt: $createdAt)'; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:developer'; | |
import 'package:alerts/red_alert_model.dart'; | |
import 'package:cloud_firestore/cloud_firestore.dart'; | |
class AlertService { | |
const AlertService(); | |
static const String _alertsCollection = 'alerts'; | |
static final CollectionReference _dbRef = | |
FirebaseFirestore.instance.collection(_alertsCollection); | |
Future<void> create(AlertModel alerts) async { | |
try { | |
await _dbRef.doc(alerts.id).set(alerts.toJson()); | |
} catch (e) { | |
log('Error creating alerts: $e'); | |
} | |
} | |
Future<void> update(AlertModel alerts) async { | |
try { | |
await _dbRef.doc(alerts.id).update(alerts.toJson()); | |
} catch (e) { | |
log('Error updating alerts: $e'); | |
} | |
} | |
Future<void> delete(String alertsId) async { | |
try { | |
await _dbRef.doc(alertsId).delete(); | |
} catch (e) { | |
log('Error deleting alerts: $e'); | |
} | |
} | |
Future<AlertModel?> getById(String alertsId) async { | |
try { | |
final snapshot = await _dbRef.doc(alertsId).get(); | |
if (!snapshot.exists) return null; | |
final json = snapshot.data(); | |
if (json is! Map<String, dynamic>) { | |
throw Exception('Invalid JSON data'); | |
} | |
return AlertModel.fromJson(json); | |
} catch (e) { | |
log('Error fetching alerts: $e'); | |
return null; | |
} | |
} | |
Stream<List<AlertModel>> getStream() { | |
return _dbRef.snapshots().map((snapshot) { | |
return snapshot.docs.map((doc) { | |
final json = doc.data(); | |
if (json is! Map<String, dynamic>) { | |
throw Exception('Invalid JSON data'); | |
} | |
return AlertModel.fromJson(json); | |
}).toList(growable: false); | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:alerts/firebase_options.dart'; | |
import 'package:cloud_firestore/cloud_firestore.dart'; | |
import 'package:firebase_core/firebase_core.dart'; | |
import 'package:flutter/material.dart'; | |
import 'firestore_service.dart'; | |
import 'red_alert_model.dart'; | |
Future<void> main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
// Firebase | |
await Firebase.initializeApp( | |
options: DefaultFirebaseOptions.currentPlatform, | |
); | |
FirebaseFirestore.instance.settings = const Settings( | |
persistenceEnabled: true, | |
); | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Alert Service Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: const AlertScreen(), | |
); | |
} | |
} | |
// TODO: DEMO ONLY | |
class AlertScreen extends StatefulWidget { | |
const AlertScreen({super.key}); | |
@override | |
State<AlertScreen> createState() => _AlertScreenState(); | |
} | |
class _AlertScreenState extends State<AlertScreen> { | |
final AlertService _alertService = const AlertService(); | |
final TextEditingController _idController = TextEditingController(); | |
final TextEditingController _titleController = TextEditingController(); | |
final TextEditingController _messageController = TextEditingController(); | |
// for updating alerts | |
DateTime? _dateTime; | |
@override | |
void dispose() { | |
_idController.dispose(); | |
_titleController.dispose(); | |
_messageController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Alert Service Demo'), | |
), | |
body: Padding( | |
padding: const EdgeInsets.all(16.0), | |
child: Column( | |
children: [ | |
TextField( | |
controller: _idController, | |
decoration: const InputDecoration(labelText: 'Alert ID'), | |
), | |
TextField( | |
controller: _titleController, | |
decoration: const InputDecoration(labelText: 'Alert Title'), | |
), | |
TextField( | |
controller: _messageController, | |
decoration: const InputDecoration(labelText: 'Alert Message'), | |
), | |
Row( | |
children: [ | |
ElevatedButton( | |
onPressed: _createAlert, | |
child: const Text('Create'), | |
), | |
const SizedBox(width: 8), | |
ElevatedButton( | |
onPressed: _updateAlert, | |
child: const Text('Update'), | |
), | |
], | |
), | |
Expanded( | |
child: StreamBuilder<List<AlertModel>>( | |
stream: _alertService.getStream(), | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.waiting) { | |
return const Center(child: CircularProgressIndicator()); | |
} | |
if (snapshot.hasError) { | |
return Center(child: Text('Error: ${snapshot.error}')); | |
} | |
final alerts = snapshot.data ?? []; | |
return ListView.builder( | |
itemCount: alerts.length, | |
itemBuilder: (context, index) { | |
final alert = alerts[index]; | |
return ListTile( | |
title: Row( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Expanded(child: Text(alert.title)), | |
const SizedBox(width: 8), | |
Text( | |
alert.timeAgo, | |
style: const TextStyle(fontSize: 12), | |
), | |
], | |
), | |
subtitle: Text(alert.message), | |
onTap: () => _fillUpdateFields(alert), | |
trailing: IconButton( | |
icon: const Icon(Icons.delete), | |
onPressed: () => _deleteAlert(alert.id), | |
), | |
); | |
}, | |
); | |
}, | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
void _fillUpdateFields(AlertModel alert) { | |
_idController.text = alert.id; | |
_titleController.text = alert.title; | |
_messageController.text = alert.message; | |
_dateTime = alert.createdAt; | |
} | |
void _createAlert() { | |
final alert = AlertModel( | |
id: _idController.text, | |
title: _titleController.text, | |
message: _messageController.text, | |
createdAt: DateTime.now(), | |
); | |
_alertService.create(alert); | |
} | |
void _updateAlert() { | |
final alert = AlertModel( | |
id: _idController.text, | |
title: _titleController.text, | |
message: _messageController.text, | |
createdAt: _dateTime!, | |
); | |
_alertService.update(alert); | |
} | |
void _deleteAlert(String id) { | |
_alertService.delete(id); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment