How to update plutogrid row data with Streambuilder #187
Unanswered
allenlim-0627
asked this question in
Q&A
Replies: 3 comments 1 reply
-
Probably because of state management. |
Beta Was this translation helpful? Give feedback.
0 replies
-
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pluto_grid/pluto_grid.dart';
abstract class Model {
Map<String, dynamic> get map => {};
Map<String, dynamic> get dataGridMap => map;
getDataGridValue(String key) => dataGridMap[key];
}
// Model of data
class ClientModel extends Model {
late final int id;
late final String fullname, phone;
late final DateTime createdAt;
ClientModel(
this.id,
this.fullname,
this.phone,
this.createdAt,
);
static DatabaseReference ref = FirebaseDatabase.instance.ref('clients');
static Future<ClientModel?> fromId(int id) async {
Map? data = (await ref.child('$id').get()).value as Map?;
return data != null ? fromMap(data) : null;
}
static Future<List<ClientModel>> all() async {
List? data = (await ref.get()).value as List?;
return data != null ? allFromMap(List<Map>.from(data)) : [];
}
static Stream<List<ClientModel>> stream() =>
ref.onValue.asyncExpand<List<ClientModel>>(
(event) async* {
if (event.snapshot.value != null) {
List data = event.snapshot.value as List;
if (data.isNotEmpty) {
yield allFromMap(data);
}
}
},
);
static List<ClientModel> allFromMap(List items) =>
[for (Map data in items) fromMap(data)];
static ClientModel fromMap(Map data) => ClientModel(
data['id'],
data['fullname'],
data['phone'],
DateTime.fromMillisecondsSinceEpoch(data['created_at']),
);
Map<String, dynamic> get map => {
'id': id,
'fullname': fullname,
'phone': phone,
'created_at': createdAt.millisecondsSinceEpoch,
};
static Future<int> getNewId() async =>
((await ref.get()).value as List?)?.length ?? 0;
static Future<ClientModel> create(Map data) async {
ClientModel item = ClientModel.fromMap(data);
await ref.child('${item.id}').set(item.map);
return item;
}
update(Map data) async {
fullname = data['fullname'] ?? fullname;
phone = data['phone'] ?? phone;
createdAt = data['created_at'] ?? createdAt;
await ref.child('$id').update(map);
}
delete() async => remove(id);
static Future remove(int id) => ref.child('$id').remove();
}
// Table Widget
class TableView extends StatelessWidget {
final TableViewController controller;
final bool Function(Model model)? avaliableModels;
final EdgeInsets? margin, padding;
const TableView({
required this.controller,
this.avaliableModels,
this.margin,
this.padding,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return PlutoGrid(
columns: controller.stateManager.columns,
// rows: rows,
rows: controller.stateManager.rows,
onChanged: (PlutoGridOnChangedEvent event) {
print(event);
},
onLoaded: (PlutoGridOnLoadedEvent event) {
controller.stateManager = event.stateManager;
controller.stateManager.setSelectingMode(PlutoGridSelectingMode.cell);
controller.stateManager.setShowColumnFilter(true);
},
configuration: const PlutoGridConfiguration(),
);
}
}
class TableViewController {
final Stream<List<Model>>? streamSource;
final LoadAction streamLoadAction;
final List<PlutoColumn> columns;
final bool Function(Model model)? avaliableModels;
late PlutoGridStateManager stateManager;
TableViewController({
List<Model> initialItems = const [],
this.streamSource,
this.streamLoadAction = LoadAction.replace,
required this.columns,
this.avaliableModels,
}) {
items = initialItems;
if (streamSource != null) {
streamSource!.listen((items) => loadItems(
items,
loadAction: streamLoadAction,
));
stateManager = PlutoGridStateManager(
columns: columns,
rows: [],
gridFocusNode: null,
scroll: null,
);
update();
}
}
List<String> get fields => [for (PlutoColumn column in columns) column.field];
List<Model> items = [];
List<Model> filterModels(List<Model> items) {
List<Model> filterdModels = [];
for (Model item in items) {
if (avaliableModels!(item)) filterdModels.add(item);
}
return filterdModels;
}
loadItems(List<Model> items, {LoadAction loadAction = LoadAction.replace}) {
items = avaliableModels != null ? filterModels(items) : items;
switch (loadAction) {
case LoadAction.addAfter:
this.items.addAll(items);
break;
case LoadAction.addBefor:
items.addAll(this.items);
this.items = items;
break;
default:
this.items = items;
}
update();
}
update() async {
stateManager.setShowLoading(true);
List<PlutoRow> refRows = [
for (Model model in items)
PlutoRow(
cells: {
for (String field in fields)
field: PlutoCell(value: model.getDataGridValue(field))
},
)
];
stateManager.refRows.clear();
stateManager.refRows.addAll(refRows);
stateManager.setShowLoading(false);
}
}
class LoadAction {
final String value;
const LoadAction(this.value);
static const LoadAction replace = LoadAction('replace');
static const LoadAction addAfter = LoadAction('add-after');
static const LoadAction addBefor = LoadAction('add-befor');
}
// usage
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _HomePageState();
}
class _HomePageState extends State<MyHomePage> {
late TextEditingController fullnameController;
late TextEditingController phoneController;
late TableViewController tableViewController;
@override
void initState() {
fullnameController = TextEditingController();
phoneController = TextEditingController();
tableViewController = TableViewController(
streamSource: ClientModel.stream(), // stream from firebase database
avaliableModels: (model) {
ClientModel client = model as ClientModel;
// filter models code
return client.createdAt.year >= 2022;
},
columns: [
// columns you want to show
PlutoColumn(
title: '#',
field: 'id', // must be equals with datagrid map keys
type: PlutoColumnType.number(),
),
PlutoColumn(
title: 'Fullname',
field: 'fullname',
type: PlutoColumnType.text(),
),
PlutoColumn(
title: 'Phone',
field: 'phone',
type: PlutoColumnType.text(),
),
],
);
super.initState();
}
@override
void dispose() {
fullnameController.dispose();
phoneController.dispose();
super.dispose();
}
addClient() async {
if (fullnameController.text.isEmpty || phoneController.text.isEmpty) {
print('please fill all data');
return;
}
// 'isPhoneNumber' from Getx package
else if (!phoneController.text.isPhoneNumber) {
print('Invalid phone number');
return;
}
await ClientModel.create({
'id': await ClientModel.getNewId(),
'fullname': fullnameController.text,
'phone': phoneController.text,
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Flex(
direction: Axis.vertical,
children: [
TextField(
controller: fullnameController,
decoration: const InputDecoration(hintText: 'Fullname'),
),
TextField(
controller: phoneController,
decoration: const InputDecoration(hintText: 'Phone'),
),
ElevatedButton(onPressed: addClient, child: const Text('add')),
Flexible(child: TableView(controller: tableViewController))
],
),
),
);
}
} |
Beta Was this translation helpful? Give feedback.
1 reply
-
My bug might help you: The bug points to my working example with firebase and Pluto Grid. Note that the workaround for this bug was to have the PlutoGrid(key: ObjectKey(rows)...) |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi, I tried to use Streambuilder to generate a list of plutoRow records, but when I try to create/update data from Firestore, I am able to get correct data inside my Streambuilder where I generate the plutoRow records, but the PlutoGrid seems like keep getting old data. Is it something to deal with the key in flutter?
This is the part where I insert plutoRow into plutoGrid
This is my repository to handle Firestore query
Thank you so much and please kindly advise on how to get the data right.
Beta Was this translation helpful? Give feedback.
All reactions