In home page/screen, i have 3 sections :
categories list view
popular food list view
new food list view
I want to load all api data whenever home page/screen load, i created methods for categories and popular food section.
But there is a problem only one funtion call in init method and one section is load but when i change any thing and save then next section load,
So what is the best way to call this api and load is UI properly.
here is my code.
Home Controller
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_admin/consts/consts.dart';
import '../api_service/api_endpoints.dart';
import '../model/category_model.dart';
import '../model/popular_recipe_model.dart';
class HomeController extends GetxController {
var currentNavIndex = 0.obs;
var isLoading = false.obs;
//category list variable
List categoryList = [].obs;
//popular recipe list variable
List popularRecipeList = [].obs;
#override
void onInit() {
fetchCategory();
fetchPopularRecipe();
super.onInit();
}
//change navigation tab index
changeIndex(index) {
currentNavIndex.value = index;
}
// fetch category
Future<List?> fetchCategory() async {
var client = http.Client();
isLoading.value = true;
try {
var response = await client.get(Uri.parse(
ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.fetchCat));
if (response.statusCode == 200) {
var jsonString = jsonDecode(response.body);
var data = jsonString['data'];
categoryList =
List.from(data).map((e) => Categories.fromJson(e)).toList();
isLoading.value = false;
return categoryList;
} else {
Get.snackbar("Error", "data not found");
return null;
}
} finally {
client.close();
}
}
// fetch popular recipe
Future<List?> fetchPopularRecipe() async {
var client = http.Client();
isLoading.value = true;
try {
var response = await client.get(Uri.parse(
ApiEndPoints.baseUrl + ApiEndPoints.authEndPoints.popularRecipe));
if (response.statusCode == 200) {
var jsonString = jsonDecode(response.body);
var data = jsonString['data'];
popularRecipeList =
List.from(data).map((e) => PopularRecipe.fromJson(e)).toList();
isLoading.value = false;
return popularRecipeList;
} else {
Get.snackbar("Error", "data not found");
return null;
}
} finally {
client.close();
}
}
}
Home Screen
// ),
Obx(()=>SizedBox(
height: Dimensions.height300,
child: controller.isLoading.value ? const Center(child: CircularProgressIndicator()) : ListView.builder(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
itemCount: controller.popularRecipeList.length,
itemBuilder: (context, index) {
return popularRecipe(
title: "${controller.popularRecipeList[index].name}",
time: "${controller.popularRecipeList[index].recipeTime}",
icon: const Icon(Icons.bookmark_outline_sharp),
img: "${controller.popularRecipeList[index].imageUrl}");
}),
),
),
Because isLoading is set to false as soon as one of the Future completes. Use different flag for them or if you want to use a single flag then call a new Function in init method and wait in that function for the futures to complete.
Future<void> loadData() async {
isLoading.value = true;
await fetchCategory();
await fetchPopularRecipe();
isLoading.value = false;
}
Also, you don't need to return the value because you are already assigning in the function.
Related
I want to fetch paginated data in my flutter app from my website using REST API.
I have integrated pagination and now it is started working.
But the problem is that on loading more data, I am getting duplicate data instead of getting new data.
I think I am doing something wrong to increment the page no. in the _getAllNews() method
Here is my complete code, and I think I am doing very small mistake in this.
class Tedd extends StatefulWidget {
#override
_TeddState createState() => _TeddState();
}
class _TeddState extends State<Tedd> {
List<NewsModel> _newsList = [];
bool isLoading = true;
int currentPage = 1;
bool hasReachedEnd = false;
ScrollController scrollController = ScrollController();
_getAllNews(page) async {
setState(() {
isLoading = true;
});
var articles = await http.get(Uri.parse(
"https://pkbhai.com/myprojects/kids-stories/api/all-stories?page=${page}"));
var result = json.decode(articles.body);
print(result);
result['data'].forEach((data) {
var news = NewsModel();
news.id = data["id"];
news.articleTitle = data["name"];
if (mounted) {
setState(() {
_newsList.add(news);
isLoading = false;
currentPage = currentPage++;
});
}
});
}
void handleNext() {
scrollController.addListener(() async {
if (scrollController.position.maxScrollExtent ==
scrollController.position.pixels) {
_getAllNews(currentPage);
}
});
}
#override
void initState() {
_getAllNews(currentPage);
handleNext();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: scrollController,
itemCount: _newsList.length + (isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index == _newsList.length) {
return Center(
child: CircularProgressIndicator(),
);
}
return Container(
height: 150, child: Text(_newsList[index].articleTitle!));
},
),
);
}
}
What am I doing wrong?
i have check the api, now the problem of the repeated data is coming from the api url.
after check the api and test base on this 3 parameter,
current_page= 1,
from=1,
last_page=3,
but is not working...
solution:
contact the developer of the api and check the api or recreate another working pagination url for you to make request
The context is we have a TextFormField and ElevatedButton. I entered the URL of the video (usually format mp4) and pressed Button. Bellow them will show videos are scraped from this URL. I think I should use the package video_player. However, in the example code, it required an initial URL on initState (or onInit if using the GetX package). How to make the URL dynamic and only show after entering the URL?
class CyberDropController extends GetxController with StateMixin<List> {
final dataCyberDrop = DataCyberDrop();
late VideoPlayerController videoController;
var url = ''.obs;
#override
void onInit() {
super.onInit();
playVideo(url.value);
change(null, status: RxStatus.empty());
}
Future<void> playVideo(String url) async {
videoController = VideoPlayerController.network(url);
await videoController.initialize();
await videoController.setLooping(true);
await videoController.play();
update();
}
void fetch({String? link}) async {
change(null, status: RxStatus.loading());
try {
var data = await dataCyberDrop.scraperCyberDrop(link: link);
change(data, status: RxStatus.success());
} catch (e) {
change(null, status: RxStatus.error(e.toString()));
}
}
}
Views:
controller.obx(
(state) => Column(
children: state!
.map(
(e) => e!.toString().contains('.mp4')
? AspectRatio(
aspectRatio: controller
.videoController.value.aspectRatio,
child: VideoPlayer(controller.videoController),
)
: Image.network(e),
)
.toList(),
since you said that you're working with TextFormField, you can use the onChanged property to execute your methods just after you set the URL:
TextFormField(
onChanged: (urlValue) async{
if(/* recommended to set a condition on URL where it shouldn't execute anything*/) {
return;
}
await controller.playVideo(urlValue);
}
)
I'm trying to track location in the background using flutter and to do so I'm using the background_locator plugin. It has been implemented in such a way that there are certain static callback functions that were registered. I've declared a class variable of File type to save the log in the background. The global variable is built at the very beginning of the class.
Issue: While invoking the callback method, the global variable built is becoming null. So though I could see the location log in my console, I couldn't write it to the file as the object is null.
Tries:
I've tried with the exact example provided in their documentation.
I've declared it as non static property and tried to access with the class object.
Tried it out declaring it as static property as well.
Tried building file object with the same path every time needed but it is throwing following issue.
No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider
Here is my complete source code for reference.
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'dart:ui';
import 'package:background_locator/background_locator.dart';
import 'package:background_locator/location_dto.dart';
import 'package:background_locator/settings/android_settings.dart';
import 'package:background_locator/settings/ios_settings.dart';
import 'package:background_locator/settings/locator_settings.dart';
import 'package:flutter/material.dart';
import 'package:location_permissions/location_permissions.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart' as ph;
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool isRunning = false;
LocationDto? lastLocation;
bool permissionsGranted = false;
static const String isolateName = 'LocatorIsolate';
static int _count = -1;
static File? finalFile;
void requestPermission() async {
var storageStatus = await ph.Permission.storage.status;
if (!storageStatus.isGranted) {
await ph.Permission.storage.request();
}
if (storageStatus.isGranted) {
permissionsGranted = true;
setPrerequisites();
}
setState(() {});
}
static Future<void> init(Map<dynamic, dynamic> params) async {
//TODO change logs
print("***********Init callback handler");
if (params.containsKey('countInit')) {
dynamic tmpCount = params['countInit'];
if (tmpCount is double) {
_count = tmpCount.toInt();
} else if (tmpCount is String) {
_count = int.parse(tmpCount);
} else if (tmpCount is int) {
_count = tmpCount;
} else {
_count = -2;
}
} else {
_count = 0;
}
print("$_count");
await setLogLabel("start");
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(null);
}
static Future<void> disposeLocationService() async {
await setLogLabel("end");
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(null);
}
static Future<void> callback(LocationDto locationDto) async {
await setLogPosition(_count, locationDto);
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(locationDto);
_count++;
}
static Future<void> setLogLabel(String label) async {
final date = DateTime.now();
await _MyAppState().writeToLogFile(
'------------\n$label: ${formatDateLog(date)}\n------------\n');
}
static Future<void> setLogPosition(int count, LocationDto data) async {
final date = DateTime.now();
await _MyAppState().writeToLogFile(
'$count : ${formatDateLog(date)} --> ${formatLog(data)} --- isMocked: ${data.isMocked}\n');
}
static double dp(double val, int places) {
num mod = pow(10.0, places);
return ((val * mod).round().toDouble() / mod);
}
static String formatDateLog(DateTime date) {
return date.hour.toString() +
":" +
date.minute.toString() +
":" +
date.second.toString();
}
static String formatLog(LocationDto locationDto) {
return dp(locationDto.latitude, 4).toString() +
" " +
dp(locationDto.longitude, 4).toString();
}
#override
void initState() {
super.initState();
if (permissionsGranted) {
setPrerequisites();
} else {
requestPermission();
}
}
void setPrerequisites() async {
finalFile = await _getTempLogFile();
if (IsolateNameServer.lookupPortByName(isolateName) != null) {
IsolateNameServer.removePortNameMapping(isolateName);
}
IsolateNameServer.registerPortWithName(port.sendPort, isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
setState(() {});
}
Future<void> updateUI(LocationDto data) async {
final log = await readLogFile();
await _updateNotificationText(data);
setState(() {
if (data != null) {
lastLocation = data;
}
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: const Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
if (await _checkLocationPermission()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
} else {
// show error
}
}
static Future<void> initCallback(Map<dynamic, dynamic> params) async {
await init(params);
}
static Future<void> disposeCallback() async {
await disposeLocationService();
}
Future<void> locationServicecallback(LocationDto locationDto) async {
await callback(locationDto);
}
static Future<void> notificationCallback() async {
print('***notificationCallback');
}
Future<void> writeToLogFile(String log) async {
await finalFile!.writeAsString(log, mode: FileMode.append);
}
Future<String> readLogFile() async {
return finalFile!.readAsString();
}
static Future<File?> _getTempLogFile() async {
File file =
File('${(await getApplicationDocumentsDirectory()).path}/log.txt');
if (file.existsSync()) {
return file;
} else {
file = await file.create(recursive: true);
}
return file;
}
Future<void> clearLogFile() async {
await finalFile!.writeAsString('');
}
Future<bool> _checkLocationPermission() async {
final access = await LocationPermissions().checkPermissionStatus();
switch (access) {
case PermissionStatus.unknown:
case PermissionStatus.denied:
case PermissionStatus.restricted:
final permission = await LocationPermissions().requestPermissions(
permissionLevel: LocationPermissionLevel.locationAlways,
);
if (permission == PermissionStatus.granted) {
return true;
} else {
return false;
}
case PermissionStatus.granted:
return true;
default:
return false;
}
}
Future<void> _startLocator() async {
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(
callback,
initCallback: initCallback,
initDataCallback: data,
disposeCallback: disposeCallback,
iosSettings: const IOSSettings(
accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0),
autoStop: false,
androidSettings: const AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback: notificationCallback,
),
),
);
}
}
Any help/suggestion would be highly appreciated. Thank you!
The callback function not getting called was an issue I faced inthe version 1.6.12.
I fixed the problem by
forking the background_locator repo on github.
cloning the repo to my computer
opened the location_dto.dart file and went to fromJson function.
added json[Keys.ARG_PROVIDER] ?? '' instead
commited and pushed to my forked repository
in pubspec.yaml, I updated my dependency to point to my forked repository as follows:
background_locator:
git:
url: git#github.com:frankvollebregt/background_locator.git
Please follow these two github issues if you find any problem:
https://github.com/rekabhq/background_locator/issues/320
https://github.com/rekabhq/background_locator/issues/301
background_locator dosen't work on latest flutter sdk versions
for me it's worked when I do this steps
Flutter sdk version should be :3.0.1
In pubspec.yaml file change sdk: ">=2.8.0 <3.0.0"
Don't migrate your code to null safety
in gradle-wrapper.properties change gradle version to gradle-6.5
android/build gradle change ext.kotlin_version to '1.4.31'
android/app/build gradle change compileSdkVersion to 31, minSdkVersion to 19 and targetSdkVersion to 30
This is not a problem with the background locator plugin. When the plugin/library is not registered with Flutter Engine, the 'No implementation' error occurs.
You have been attempting to access the path provider methods from within a Background Isolate. Normally, the path provider plugin will be registered with main isolate.
If you want to use it in your background isolate, you must manually register it with the engine.
Follow the steps below and add these two functions to the Init function in location_service_repositary.dart
if (Platform.isAndroid) PathProviderAndroid.registerWith();
if (Platform.isIOS) PathProviderIOS.registerWith();
Have a good day.
I am trying to make a Telegram client for android using the tdlib flutter port. I am currently attempting to make a contact list of sorts, by requesting it from telegram and making a listview of textbuttons.
The only issue is that since the library is async, I get the contact list after the layout has been initialized. Is it possible to somehow rebuild the layout or update it to make the list load properly.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fima/services/telegram_service.dart';
import 'package:tdlib/td_api.dart' show TdError;
import 'package:provider/provider.dart';
import 'package:tdlib/td_api.dart' as TdApi;
class ContactListScreen extends StatefulWidget {
#override
_ContactListScreenState createState() => _ContactListScreenState();
}
class _ContactListScreenState extends State<ContactListScreen> {
final String title = 'Contact list';
bool _loadingStep = false;
String _Error;
String route = "initRoute";
List<TextButton> contacts = [];
#override
void initState() {
super.initState();
_getContacts(onError: _handelError,);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
backgroundColor: Color(0xD3232323),
),
body: Container(
child:
ListView (
children: contacts,
),
),
);
}
Future _getContacts(
{
void Function(TdError) onError,
}) async {
final result = await context.read<TelegramService>().send(
TdApi.GetContacts(
),
);
if (result is TdError && onError != null) {
onError(result);
}
TdApi.Users users = result;
for (var i = 0; i < users.totalCount; i++) {
final result = await context.read<TelegramService>().send(
TdApi.GetUser(userId: users.userIds[i]),
);
TdApi.User user = result;
print(user.firstName + " " + user.lastName);
final contact = TextButton(
onPressed: () {
print("Test");
},
child: Text(user.firstName + " " + user.lastName),
);
setState(() {
contacts.add(contact);
});
}
}
void _handelError(TdError error) async {
setState(() {
_loadingStep = false;
_Error = error.message;
});
}
}
I have attempted to use setState, but without much success, could anyone be so kind as to provide me with the solution to this problem?
Using the FutureBuilder might help. It is a widget that builds itself based on the latest snapshot of interaction with a Future.
You can modify your build to return a FutureBuilder something like this:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getContacts,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
//Use snapshot data to build by returning your Container with List
}
else{
//Return a CircularProgressIndicator
}
}
}
Refer the documentation on the FutureBuilder class here.
void _myMatches() {
if (SignUp.userUid != null) {
FirebaseFirestore.instance
.collection("posts")
.where(
'owner id',
isEqualTo: SignUp.userUid,
)
.where("User Id", isNotEqualTo: [])
.where("rental status", isEqualTo: false)
.get()
.then((value) {
value.docs.forEach((result) {
print(result.data());
});
});
} else {
FirebaseFirestore.instance
.collection("posts")
.where(
'owner id',
isEqualTo: Loginpage.userUid,
)
.where("User Id", isNotEqualTo: [])
.where("rental status", isEqualTo: false)
.get()
.then((value) {
value.docs.forEach((result) {
print(result.data());
});
});
}
}
}
Hi, I am using flutter and firestore to write a program. My function that reads the data is as follows:(mentioned above)
which i call when a specific button is pressed. This leads to the data being read from firestore to be printed on the console. What do I do to display it on my emulator. How do I wrap this data in a widget so I can display it on the screen on whichever page i want?
The key is to use a FutureBuilder to render UI after you get the data, and show loading before that. Then inside builder of FutureBuilder, use ListView and ListTile(or anything you like) to render list items.
A minimum example might looks like this:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
home: App(),
));
}
class App extends StatelessWidget {
Future<QuerySnapshot<Map<String, dynamic>>> getData() {
// Handle any data retrieval logic you want
return FirebaseFirestore.instance.collection('posts').get();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot<Map<String, dynamic>>>(
// plug your future snapshot here
future: getData(),
builder: (context, snapshot) {
// Check loading
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
// Check error
final queryData = snapshot.data;
if (snapshot.hasError || queryData == null) {
return Icon(Icons.error);
}
return Scaffold(
// Use ListView.builder to render only visible items
body: ListView.builder(
itemCount: queryData.docs.length,
itemBuilder: (context, index) {
// Get data inside docs
final docData = queryData.docs[index].data();
return ListTile(
title: docData['title'],
subtitle: docData['subtitle'],
);
},
),
);
});
}
}