Skip to content

Instantly share code, notes, and snippets.

@zerebral
Created December 23, 2022 00:46
Show Gist options
  • Save zerebral/d934622f6c0ad87f2ba2e35ceea21144 to your computer and use it in GitHub Desktop.
Save zerebral/d934622f6c0ad87f2ba2e35ceea21144 to your computer and use it in GitHub Desktop.
Infinite Scroll Widget + Algolia + FlutterFlow
// Automatic FlutterFlow imports
import '../../backend/backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import '../widgets/index.dart'; // Imports other custom widgets
import '../actions/index.dart'; // Imports custom actions
import '../../flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
//import 'index.dart'; // Imports other custom widgets
import 'package:provider/provider.dart';
import 'dart:convert';
import 'package:around/components/user_post_card_widget.dart';
import '../../flutter_flow/custom_functions.dart' as functions;
import 'package:http/http.dart' as http;
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
class CustomPostList extends StatefulWidget {
const CustomPostList(
{Key? key,
this.width = double.infinity,
this.height = double.infinity,
this.strokeWidth = 2})
: super(key: key);
final double width;
final double height;
final double strokeWidth;
// final String location;
// final String searchQuery;
// final int radius;
@override
_CustomPostListState createState() => _CustomPostListState();
}
class _CustomPostListState extends State<CustomPostList> {
ValueNotifier<String> _searchQuery =
ValueNotifier<String>(FFAppState().postsearch);
ValueNotifier<String> _location =
ValueNotifier<String>(FFAppState().searchLocation);
ValueNotifier<int> _radius = ValueNotifier<int>(FFAppState().searchRadius);
static const _pageSize = 10;
final PagingController<int, Post> _pagingController =
PagingController(firstPageKey: 0);
@override
void initState() {
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
_searchQuery.addListener(() => {_pagingController.refresh()});
_location.addListener(() => {_pagingController.refresh()});
_radius.addListener(() => {_pagingController.refresh()});
super.initState();
}
Future<List<Post>> fetch(page) async {
List<Post> posts = [];
var url = "https://<your-algolia-app-id>-dsn.algolia.net/1/indexes/userposts/query";
final response = await http.post(Uri.parse(url),
body: jsonEncode({
"params":
"hitsPerPage=${_pageSize}&page=${page}&query=${FFAppState().postsearch}&aroundLatLng=${FFAppState().searchLocation}&aroundRadius=25000"
}),
headers: {
'X-Algolia-API-Key': '<your-algolia-api-key>',
'X-Algolia-Application-Id': '<your-algolia-app-id>'
});
if (response.statusCode == 200) {
Map<String, dynamic> jsonMap = json.decode(response.body);
(jsonMap['hits'] as List).forEach((x) => {posts.add(Post.fromJson(x))});
return posts;
} else {
//throw Exception('Failed to load posts');
return posts;
}
}
Future<void> _fetchPage(int pageKey) async {
try {
double page = pageKey / _pageSize;
List<Post> newItems = await fetch(page);
print(newItems.length);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
_pagingController.appendLastPage(newItems);
} else {
final nextPageKey = pageKey + newItems.length;
_pagingController.appendPage(newItems, nextPageKey);
}
} catch (error) {
_pagingController.error = error;
}
}
@override
Widget build(BuildContext context) {
context.watch<FFAppState>();
_searchQuery.value = FFAppState().postsearch;
_location.value = FFAppState().searchLocation;
_radius.value = FFAppState().searchRadius;
return PagedListView<int, Post>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<Post>(
itemBuilder: (context, item, index) => UserPostCardWidget(
postRef: functions.getUserPostsDocRef(item.objectId)),
));
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
}
class Post {
final String objectId;
const Post({required this.objectId});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(objectId: json['objectID']);
}
}
@akserikawa
Copy link

akserikawa commented Jun 30, 2023

Thank you for posting this one! It helps a lot. Just a quick question: How did you manage to import your custom UserPostCardWidget?
FF gives me errors when trying to import my own widgets into this one.

import 'package:around/components/user_post_card_widget.dart';

is "around" the name of your project in the pubspec.yaml?

@zerebral
Copy link
Author

Thats right 'around' is the name of the project in pubspec

@zerebral
Copy link
Author

I am hand coding a lot of stuff outside FF now though and have only used FF to build UI for most part now. It comes in handy for quickly building the UI with database / API connectivity, but found some advanced features lacking and those are easier to hand code outside FF.

@zerebral
Copy link
Author

Also you may want to wrap your row widget inside KeepAliveWidgetWrapper so that the list does not flicker, especially when you scroll up.

@akserikawa
Copy link

Thanks for the advice!
Regarding the path in your import on line 16. Have you ever gotten this error?

"Target of URI doesn't exist: package:{name}/components/{component_name}.dart

It's weird that FF can't compile the component although if I download the code and run it locally from my own flutter installation on vscode it works just fine :/

@zerebral
Copy link
Author

You should reach out to FF support.. I ditched it, as like these there were many instances where I needed help and very little of it was available.. The FF support experience was average too throughout! Its much easier and faster to develop with VSC and few Flutter plugins there (along with FF used just to build the UI)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment