I am attempting to combine two streams using RxDart and display the stream within a ListView.
I modeled this technique based on this post How do I join data from two Firestore collections in Flutter?, however, when I run this code, I am returning an empty dataset.
Any help would be greatly appreciated!
import 'package:rxdart/rxdart.dart';
class _HomeScreenState extends State<HomeScreen> {
Stream<List<CombineStream>> _combineStream;
String currentUserId = 'id12345'
#override
void initState() {
super.initState();
_combineStream =
usersChatsRef.child(currentUserId).onValue.map((userChatStream) {
return (userChatStream.snapshot.value).map((f) {
Stream<UsersChats> uDetail =
f.map<UsersChats>((node) => UsersChats.fromMap(node));
Stream<Chat> sDetail = chatsDetailsRef
.child(f.key)
.onValue
.map<Chat>((node2) => Chat.fromMap(node2.snapshot.value));
return Rx.combineLatest2(
uDetail, sDetail, (u, s) => CombineStream(u, s));
});
}).switchMap((streams) {
return (streams.length) > 0
? streams.combineLatestList(streams)
: streams.just([]);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: StreamBuilder(
stream: _combineStream,
builder:
(BuildContext context, AsyncSnapshot<List<CombineStream>> snap) {
if (snap.hasData && !snap.hasError) {
return ListView.builder(
itemCount: snap.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snap.data[index].combineUserChats.id),
subtitle: Text(myChats[index].combineUserChats.id),
);
},
);
} else {
return Center(child: Text("No data"));
}
},
),
);
}
}
class CombineStream {
final UsersChats combineUserChats;
final Chat combineChat;
CombineStream(this.combineUserChats, this.combineChat);
}
class Chat {
String id;
String name;
Chat({String id, String name}) {
this.id = id;
this.name = name;
}
factory Chat.fromMap(Map data) {
return Chat(
name: data['name'] ?? '');
}
}
class UsersChats {
String id;
String lastUpdate;
UsersChats(
{this.id,
this.lastUpdate});
factory UsersChats.fromMap(MapEntry<dynamic, dynamic> data) {
return UsersChats(
id: data.key ?? '',
lastUpdate: data.value['lastUpdate'] ?? '');
}
}
Related
class CalendarEvents extends StatefulWidget {
#override
CalendarEventsState createState() => CalendarEventsState();
}
class CalendarEventsState extends State<CalendarEvents> {
final GoogleSignIn _googleSignIn = GoogleSignIn(
clientId:
'571891879876-rcjtmo4lds0qlhvi9ar5sk78gubnub7f.apps.googleusercontent.com',
scopes: <String>[
googleAPI.CalendarApi.calendarScope,
],
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text('Event Calendar'),
),
body: Container(
child: FutureBuilder(
future: getGoogleEventsData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
child: Stack(
children: [
Container(
child: SfCalendar(
view: CalendarView.month,
initialDisplayDate: DateTime(2020,7,15,9,0,0),
dataSource: GoogleDataSource(events: snapshot.data),
monthViewSettings: MonthViewSettings(
appointmentDisplayMode:
MonthAppointmentDisplayMode.appointment),
),
),
snapshot.data != null
? Container()
: Center(
child: CircularProgressIndicator(),
)
],
));
},
),
),
);
}
#override
void dispose(){
if(_googleSignIn.currentUser != null) {
_googleSignIn.disconnect();
_googleSignIn.signOut();
}
super.dispose();
}
Future<List<googleAPI.Event>> getGoogleEventsData() async {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
final GoogleAPIClient httpClient =
GoogleAPIClient(await googleUser!.authHeaders);
final googleAPI.CalendarApi calendarAPI = googleAPI.CalendarApi(httpClient);
final googleAPI.Events calEvents = await calendarAPI.events.list(
"primary",
);
final List<googleAPI.Event> appointments = <googleAPI.Event>[];
if (calEvents.items != null) {
for (int i = 0; i < calEvents.items!.length; i++) {
final googleAPI.Event event = calEvents.items![i];
if (event.start == null) {
continue;
}
appointments.add(event);
}
}
return appointments;
}
}
class GoogleDataSource extends CalendarDataSource {
GoogleDataSource({required List<googleAPI.Event>? events}) {
this.appointments = events;
}
#override
DateTime getStartTime(int index) {
final googleAPI.Event event = appointments![index];
return event.start!.date ?? event.start!.dateTime!.toLocal();
}
#override
bool isAllDay(int index) {
return appointments![index].start.date != null;
}
#override
DateTime getEndTime(int index) {
final googleAPI.Event event = appointments![index];
return event.endTimeUnspecified != null && event.endTimeUnspecified!
? (event.start!.date ?? event.start!.dateTime!.toLocal())
: (event.end!.date != null
? event.end!.date!.add(Duration(days: -1))
: event.end!.dateTime!.toLocal());
}
#override
String getLocation(int index) {
return appointments![index].location;
}
#override
String getNotes(int index) {
return appointments![index].description;
}
#override
String getSubject(int index) {
final googleAPI.Event event = appointments![index];
return event.summary!;
}
}
class GoogleAPIClient extends IOClient {
Map<String, String> _headers;
GoogleAPIClient(this._headers) : super();
#override
Future<IOStreamedResponse> send(BaseRequest request) =>
super.send(request..headers.addAll(_headers));
#override
Future<Response> head(Object url, { Map<String, String>? headers}) {
var uri = Uri.parse("$url");
super.head(uri, headers: headers!..addAll(_headers));
throw '';
}
}
I want to retrieve all the google calendar events that are happening in google id. I want to display all the events in the syncfusion calendar with the help of firebase and google calendar api. I found this code on syncfusion blog. But it isn't working and only circularprogressindicator shows upon the screen after google login. Can someone check the code?
I have this Flutter bloc that takes a Firebase stream of restaurants and depending on the position relative to the user will filter only the closest ones depending on the restaurant location. It works fine but I have to refresh with a RefreshIndicator if I want to see any changes in restaurant documents. What am I missing? Thanks in advance.
class NearestRestaurant {
final String id;
final Restaurant restaurant;
final double distance;
NearestRestaurant({this.id, this.restaurant, this.distance});
}
class NearRestaurantBloc {
final Future<List<Restaurant>> source;
final Position userCoordinates;
final _stream = StreamController<List<Restaurant>>();
NearRestaurantBloc({
this.source,
this.userCoordinates,
}) {
List<Restaurant> resList = List<Restaurant>();
source.then((rest) {
rest.forEach((res) async {
await Geolocator().distanceBetween(
userCoordinates.latitude,
userCoordinates.longitude,
res.coordinates.latitude,
res.coordinates.longitude,
).then((distance) {
if (res.active && distance < res.deliveryRadius) {
resList.add(res);
}
});
_stream.add(resList);
});
});
}
Stream<List<Restaurant>> get stream => _stream.stream;
void dispose() {
_stream.close();
}
}
class RestaurantQuery extends StatefulWidget {
#override
_RestaurantQueryState createState() => _RestaurantQueryState();
}
class _RestaurantQueryState extends State<RestaurantQuery> {
NearRestaurantBloc bloc;
#override
Widget build(BuildContext context) {
final database = Provider.of<Database>(context, listen: true);
final session = Provider.of<Session>(context);
final userCoordinates = session.position;
bloc = NearRestaurantBloc(
source: database.patronRestaurants(),
userCoordinates: userCoordinates,
);
return StreamBuilder<List<Restaurant>>(
stream: bloc.stream,
builder: (context, snapshot) {
bool stillLoading = true;
var restaurantList = List<Restaurant>();
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData && snapshot.data.length > 0) {
restaurantList = snapshot.data;
}
stillLoading = false;
}
return Scaffold(
appBar: AppBar(
title: Text(
'Restaurants near you',
style: TextStyle(color: Theme.of(context).appBarTheme.color),
),
elevation: 2.0,
),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: RefreshIndicator(
onRefresh: () async {
setState(() {
});
},
child: RestaurantList(
nearbyRestaurantsList: restaurantList,
stillLoading: stillLoading,
),
),
);
},
);
}
#override
void dispose() {
bloc.dispose();
super.dispose();
}
}
In the build method under _RestaurantQueryState, you are returning the scaffold outside the builder method. Initially, restaurantList is null. Therefore, you don't produce the list. Whenever the stream updates, you get the snapshot data to update the restaurantList.
The problem occurs here. Even though the restaurantList is updated, the widget RestaurantList is not updated because it is outside the builder method. You can use the following code. Here we create a Widget that holds the RestaurantList widget. The widget gets updated whenever the stream updates.
class _RestaurantQueryState extends State<RestaurantQuery> {
NearRestaurantBloc bloc;
#override
Widget build(BuildContext context) {
final database = Provider.of<Database>(context, listen: true);
final session = Provider.of<Session>(context);
final userCoordinates = session.position;
//////////////////////////////////
//initialize RestaurantList widget
//////////////////////////////////
Widget restaurantWidget = RestaurantList(
nearbyRestaurantsList: [],
stillLoading: false,
);
bloc = NearRestaurantBloc(
source: database.patronRestaurants(),
userCoordinates: userCoordinates,
);
return StreamBuilder<List<Restaurant>>(
stream: bloc.stream,
builder: (context, snapshot) {
bool stillLoading = true;
var restaurantList = List<Restaurant>();
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData && snapshot.data.length > 0) {
restaurantList = snapshot.data;
/////////////////////////////
//update the restaurant widget
//////////////////////////////
restaurantWidget = RestaurantList(
nearbyRestaurantsList: restaurantList,
stillLoading: stillLoading,
);
}
stillLoading = false;
}
return Scaffold(
appBar: AppBar(
title: Text(
'Restaurants near you',
style: TextStyle(color: Theme.of(context).appBarTheme.color),
),
elevation: 2.0,
),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
///////////////////////////
//use the restaurant Widget
///////////////////////////
body: restaurantWidget,
),
);
},
);
}
My mistake. I was listening to a future instead of a stream. Here's the updated bloc code:
class NearestRestaurant {
final String id;
final Restaurant restaurant;
final double distance;
NearestRestaurant({this.id, this.restaurant, this.distance});
}
class NearRestaurantBloc {
final Stream<List<Restaurant>> source;
final Position userCoordinates;
final _stream = StreamController<List<Restaurant>>();
NearRestaurantBloc({
this.source,
this.userCoordinates,
}) {
List<Restaurant> resList = List<Restaurant>();
source.forEach((rest) {
resList.clear();
rest.forEach((res) async {
await Geolocator().distanceBetween(
userCoordinates.latitude,
userCoordinates.longitude,
res.coordinates.latitude,
res.coordinates.longitude,
).then((distance) {
if (res.active && distance < res.deliveryRadius) {
resList.add(res);
}
});
_stream.add(resList);
});
});
}
Stream<List<Restaurant>> get stream => _stream.stream;
}
I'm currently trying to build a Flutter app using BloC and have encountered some problems i hope you guys could help me with! I'm new to BloC in general so please be understanding.
I have created a BloC for adding, deleting, updating and listing data from firestore, which works fine. What i do need some help with is what i need to do in order to make my code query only entries created on the same day as i pick in my calendar widget with the pickedDate variable i got.
I want the ListView to generate tasks to its correct day, so for example if 2020-05-19 has 2 tasks, it should appear two tasks when picking that day in the calendar. I don't really know what i need to change or do on the calendar in order to achieve this since i still think BloC is a bit confusing.
Do i have to make a seperate bloc for the Calendar or is it okey to use the same bloc that handles my tasks?
Maybe it's just something small i forget to do but i feel completely lost at the moment. Currently i fetch all the firestore entries to a ListView just to see if it works, like this:
Widget _buildCalendarListView(BuildContext context) {
return BlocBuilder<TasksBloc, TasksState>(builder: (context, state) {
if (state is TasksLoading) {
return CircularProgressIndicator();
}
if (state is TasksLoaded) {
return Expanded(
child: ListView.builder(
itemCount: state.tasks.length,
itemBuilder: (context, index) {
var task = state.tasks[index];
return ListTile(
title: Text(task.description),
);
},
),
);
} else {
return Text('No tasks');
}
});
}
And here is my Calendar widget:
Widget _buildTableCalendar() {
return TableCalendar(
onDaySelected: (DateTime day, List events) {
pickedDate = day;
},
calendarController: _calendarController,
startingDayOfWeek: StartingDayOfWeek.monday,
calendarStyle: CalendarStyle(
selectedColor: Colors.blue,
todayColor: Colors.blue[200],
),
);
}
and my BloC for the Tasks where i pass datetime right now:
class TasksBloc extends Bloc<TasksEvent, TasksState> {
final TaskRepository _taskRepository;
StreamSubscription _taskSubscription;
TasksBloc({#required TaskRepository taskRepository})
: assert(taskRepository != null),
_taskRepository = taskRepository;
#override
TasksState get initialState => TasksLoading();
#override
Stream<TasksState> mapEventToState(
TasksEvent event,
) async* {
if (event is LoadTasks) {
yield* _mapLoadTasksToState(DateTime.now());
} else if (event is AddTask) {
yield* _mapAddTaskToState(event);
} else if (event is UpdateTask) {
yield* _mapUpdateTaskToState(event);
} else if (event is DeleteTask) {
yield* _mapDeleteTaskToState(event);
} else if (event is TasksUpdated) {
yield* _mapTaskUpdatedToState(event);
}
}
Stream<TasksState> _mapLoadTasksToState(DateTime queryDate) async* {
_taskSubscription?.cancel();
_taskSubscription = _taskRepository.tasks(DateTime.now()).listen((tasks) {
add(
TasksUpdated(tasks),
);
});
}
Stream<TasksState> _mapAddTaskToState(AddTask event) async* {
_taskRepository.addTask(event.task);
}
Stream<TasksState> _mapUpdateTaskToState(UpdateTask event) async* {
_taskRepository.updateTask(event.updatedTask);
}
Stream<TasksState> _mapDeleteTaskToState(DeleteTask event) async* {
_taskRepository.deleteTask(event.task);
}
Stream<TasksState> _mapTaskUpdatedToState(TasksUpdated event) async* {
yield TasksLoaded(event.tasks);
}
}
and here's the repository i created for firestore:
class FirebaseTaskRepository extends TaskRepository {
final taskCollection = Firestore.instance.collection('tasks');
#override
Future<void> addTask(Task task) {
return taskCollection.add(task.toEntity().toDocument());
}
#override
Future<void> deleteTask(Task task) {
return taskCollection.document(task.id).delete();
}
/* #override
Stream<List<Task>> tasks() {
return taskCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Task.fromEntity(TaskEntity.fromJson(doc.data)))
.toList();
});
} */
#override
Stream<List<Task>> tasks(DateTime queryDate) {
return taskCollection
.where('date', isGreaterThan: queryDate)
.snapshots()
.map((snapshot) {
return snapshot.documents
.map((doc) => Task.fromEntity(TaskEntity.fromJson(doc.data)))
.toList();
});
}
#override
Future<void> updateTask(Task update) {
return taskCollection
.document(update.id)
.updateData(update.toEntity().toDocument());
}
}
And also my Main.dart, where i use MultiBlocProvider which im not sure if i use correctly and there for i can't achieve what i want? :
void main() {
WidgetsFlutterBinding.ensureInitialized();
BlocSupervisor.delegate = MyBlocDelegate();
final UserRepository userRepository = UserRepository();
runApp(
BlocProvider(
create: (context) =>
AuthenticationBloc(userRepository: userRepository)..add(AppStarted()),
child: App(userRepository: userRepository),
),
);
}
class App extends StatelessWidget {
final UserRepository _userRepository;
App({Key key, #required UserRepository userRepository})
: assert(userRepository != null),
_userRepository = userRepository,
super(key: key);
/* #override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Unauthenticated) {
return LoginScreen(userRepository: _userRepository);
}
if (state is Authenticated) {
return HomePage(name: state.displayName);
}
return SplashScreen();
},
),
);
} */
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<TasksBloc>(
create: (context) {
return TasksBloc(
taskRepository: FirebaseTaskRepository(),
)..add(LoadTasks());
},
)
],
child: MaterialApp(
routes: {
'/': (context) {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Unauthenticated) {
return LoginScreen(userRepository: _userRepository);
}
if (state is Authenticated) {
return HomePage(name: state.displayName);
}
return SplashScreen();
},
);
},
'/addTask': (context) {
return NewTaskScreen(
onSave: (task, description, date) {
BlocProvider.of<TasksBloc>(context).add(
AddTask(
Task(task, description: description, date: date),
),
);
},
);
},
'/calendarScreen': (context) {
return CalendarScreen();
}
},
),
);
}
}
I am trying to paginate in Flutter with firebase realtime databse.
I have tried this in Firestore and it works fine there but I want this with realtime database.
I am fetching data for the first time like this.
Widget buildListMessage() {
return Flexible(
child: StreamBuilder(
stream: _firebase.firebaseDB
.reference()
.child("chats")
.child("nsbcalculator")
.orderByChild('timestamp')
.limitToFirst(15)
.onValue,
builder: (context, AsyncSnapshot<Event> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor)));
} else {
if (snapshot.data.snapshot.value != null) {
listMessage = Map.from(snapshot.data.snapshot.value)
.values
.toList()
..sort(
(a, b) => a['timestamp'].compareTo(b['timestamp']));
if (lastVisible == null) {
lastVisible = listMessage.last;
listMessage.removeLast();
}
}
return ListView.builder(
...
);
}
},
),
);
}
After that to paginate I am using a listener with ScrollController
void _scrollListener() async {
if (listScrollController.position.pixels ==
listScrollController.position.maxScrollExtent) {
_fetchMore();
}
}
and finally
_fetchMore() {
_firebase.firebaseDB
.reference()
.child("chats")
.child("nsbcalculator")
.orderByChild('timestamp')
.startAt(lastVisible['timestamp'])
.limitToFirst(5)
.once()
.then((snapshot) {
List snapList = Map.from(snapshot.value).values.toList()
..sort((a, b) => a['timestamp'].compareTo(b['timestamp']));
if (snapList.isNotEmpty) {
print(snapList.length.toString());
if (!noMore) {
listMessage.removeLast();
//Problem is here.....??
setState(() {
listMessage..addAll(snapList);
});
lastVisible = snapList.last;
print(lastVisible['content']);
}
if (snapList.length < 5) {
noMore = true;
}
}
});
}
Its working fine as realtime communication but when I try to paginate in _fetchMore() setState is called but it refreshes the state of whole widget and restarts the StreamBuilder again and all data is replaced by only new query. How can I prevent this??
Calling setState will redraw your whole widget and your list view. Now, since you supplying the steam that provides the first page, after redraw it just loads it. To avoid that you could use your own stream and supply new content to it. Then your StreamBuilder will handle the update automatically.
You need to store the full list of your items as a separate variable, update it and then sink to your stream.
final _list = List<Event>();
final _listController = StreamController<List<Event>>.broadcast();
Stream<List<Event>> get listStream => _listController.stream;
#override
void initState() {
super.initState();
// Here you need to load your first page and then add to your stream
...
_list.addAll(firstPageItems);
_listController.sink.add(_list);
}
#override
void dispose() {
super.dispose();
}
Widget buildListMessage() {
return Flexible(
child: StreamBuilder(
stream: listStream
...
}
_fetchMore() {
...
// Do your fetch and then just add items to the stream
_list.addAll(snapList);
_listController.sink.add(_list);
...
}
Try this one
pagination for RealTime list
class FireStoreRepository {
final CollectionReference _chatCollectionReference =
Firestore.instance.collection('Chat');
final StreamController<List<ChatModel>> _chatController =
StreamController<List<ChatModel>>.broadcast();
List<List<ChatModel>> _allPagedResults = List<List<ChatModel>>();
static const int chatLimit = 10;
DocumentSnapshot _lastDocument;
bool _hasMoreData = true;
Stream listenToChatsRealTime() {
_requestChats();
return _chatController.stream;
}
void _requestChats() {
var pagechatQuery = _chatCollectionReference
.orderBy('timestamp', descending: true)
.limit(chatLimit);
if (_lastDocument != null) {
pagechatQuery =
pagechatQuery.startAfterDocument(_lastDocument);
}
if (!_hasMoreData) return;
var currentRequestIndex = _allPagedResults.length;
pagechatQuery.snapshots().listen(
(snapshot) {
if (snapshot.documents.isNotEmpty) {
var generalChats = snapshot.documents
.map((snapshot) => ChatModel.fromMap(snapshot.data))
.toList();
var pageExists = currentRequestIndex < _allPagedResults.length;
if (pageExists) {
_allPagedResults[currentRequestIndex] = generalChats;
} else {
_allPagedResults.add(generalChats);
}
var allChats = _allPagedResults.fold<List<ChatModel>>(
List<ChatModel>(),
(initialValue, pageItems) => initialValue..addAll(pageItems));
_chatController.add(allChats);
if (currentRequestIndex == _allPagedResults.length - 1) {
_lastDocument = snapshot.documents.last;
}
_hasMoreData = generalChats.length == chatLimit;
}
},
);
}
void requestMoreData() => _requestChats();
}
ChatListView
class ChatView extends StatefulWidget {
ChatView({Key key}) : super(key: key);
#override
_ChatViewState createState() => _ChatViewState();
}
class _ChatViewState extends State<ChatView> {
FireStoreRepository _fireStoreRepository;
final ScrollController _listScrollController = new ScrollController();
#override
void initState() {
super.initState();
_fireStoreRepository = FireStoreRepository();
_listScrollController.addListener(_scrollListener);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Flexible(
child: StreamBuilder<List<ChatModel>>(
stream: _fireStoreRepository.listenToChatsRealTime(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
controller: _listScrollController,
shrinkWrap: true,
reverse: true,
itemBuilder: (context, index) {
...
}
);
}
)
),
);
}
void _scrollListener() {
if (_listScrollController.offset >=
_listScrollController.position.maxScrollExtent &&
!_listScrollController.position.outOfRange) {
_fireStoreRepository.requestMoreData();
}
}
}
ChatModel Class
class ChatModel {
final String userID;
final String message;
final DateTime timeStamp;
ChatModel({this.userID, this.message, this.timeStamp});
//send
Map<String, dynamic> toMap() {
return {
'userid': userID,
'message': message,
'timestamp': timeStamp,
};
}
//fetch
static ChatModel fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return ChatModel(
userID: map['userid'],
message: map['message'],
timeStamp: DateTime.fromMicrosecondsSinceEpoch(map['timestamp'] * 1000),
);
}
}
I am getting an error message saying that genre_id is null when I pass an id (integer data) through pages. So there is something wrong when I use the index as a variable to pass on an id to the next page. How do I use index to pass it as an id for the next page?
In my sqlite database that I am using, I have a tbl_genres and a tbl_books with the book entries being tied to the genre table with a genre_id (a column in both tables).
#override
Widget build(BuildContext context) {
return Scaffold(
body: !loading ? new ListView.builder(
itemCount: genreList.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new ListTile(
title: new Text("${genreList[index]}"),
onTap: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) =>
BookListPage(id: index), //how to pass index as an int?
),
);
}),
);
},
) : CircularProgressIndicator(),
);
}
Here is my next page...
class BookListPage extends StatefulWidget {
int id;
BookListPage({this.id});
#override
_BookListPageState createState() => _BookListPageState();
}
class _BookListPageState extends State<BookListPage> {
bool loading;
List<Map> bookNames;
final int id;
_BookListPageState({this.id});
void initState() {
super.initState();
loading = true;
getBookData();
}
Future getBookData() async {
print(id);
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "asset_sample_sqlite.db");
ByteData data = await rootBundle.load(join("assets", "sample_sqlite.db"));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes);
Database db = await openDatabase(path);
final _bookList = await db.rawQuery('SELECT book_name[] FROM tbl_books WHERE genre_id = $id'); //how to reference the passed id?
await db.close();
setState((){
loading = false;
bookNames = _bookList;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !loading ? new ListView.builder(
itemCount: bookNames.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new ListTile(
title: new Text("${bookNames[index]}"),
),
);
}
) : CircularProgressIndicator(),
);
}
}
And also how do I use that index in the rawQuery to display information only relating to that id?
Modify your second page as follows:
class BookListPage extends StatefulWidget {
final int id;
BookListPage({this.id});
#override
_BookListPageState createState() => _BookListPageState();
}
class _BookListPageState extends State<BookListPage> {
bool loading;
List<Map> bookNames;
_BookListPageState();
void initState() {
super.initState();
loading = true;
getBookData();
}
Future getBookData() async {
print(widget.id);
Directory documentsDirectory = await getApplicationDocumentsDirectory();