Scroll end detect to many times in flutter - flutter

I uses Listview.builder. it detect scroll end to many time that is why API call to many times and add duplicate Data in Listview.
Code:-
ListView.builder(
controller: _scrollController
..addListener(() async {
if (_scrollController
.position.pixels -
10 ==
_scrollController.position
.maxScrollExtent -
10 &&
!state.isPaginationLoading) {
print("Scroll End TEst Screen");
await ctx
.read<ProfileCubit>()
.getProfiles(
context, true, null);
}

Dont put logic code inside build. In your case _scrollController will addListener every times widget build called, cause multiple handle will trigger.
Advice for you is create and put handle logic to a function, put addListener/removeListener in initState/dispose because they was called only once.
With your problem, you can create a variale to check api was called yet and prevent other call.
class AppState extends State<App> {
var scroll = ScrollController();
var preventCall = false;
#override
initState() {
scroll.addListener(onScroll);
super.initState();
}
#override
void dispose() {
scroll.removeListener(onScroll);
super.dispose();
}
Future yourFuture() async {}
void onScroll() {
var position = scroll.position.pixels;
if (position >= scroll.position.maxScrollExtent - 10) {
if (!preventCall) {
yourFuture().then((_) => preventCall = false);
preventCall = true;
}
}
}
#override
Widget build(BuildContext context) {
return ...
}
}

You can add a condition to check if API call is happening or not and based on it you can you can call the API. You would also need to handle pagination logic if all info is loaded.

you can always check if you reached at the limit of max Limit of your scroll controller then you can call API
condition is like
child: ListView.builder(
controller: _controller
..addListener(() async {
if (_controller.position.pixels >
_controller.position.maxScrollExtent) {
_loadMore();
}
}),
you may be like full example
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
class Paggination2HomeScreen extends StatefulWidget {
const Paggination2HomeScreen({Key? key}) : super(key: key);
#override
State<Paggination2HomeScreen> createState() => _Paggination2HomeScreenState();
}
class _Paggination2HomeScreenState extends State<Paggination2HomeScreen> {
final String _baseUrl = "https://jsonplaceholder.typicode.com/photos";
int _page = 1;
final int _limit = 10;
bool _hasNextPage = true;
bool _isFirstLoadRunning = false;
bool _isLoadMoreRunning = false;
List _posts = [];
void _firstLoad() async {
setState(() {
_isFirstLoadRunning = true;
});
try {
final res = await http.get(
Uri.parse("$_baseUrl?_page=$_page&_limit=$_limit"),
);
if (res.statusCode == 200) {
setState(() {
_posts = jsonDecode(res.body);
});
log('receivedData : ${jsonDecode(res.body)}');
}
} catch (e) {
if (kDebugMode) {
log('Something went wrong');
}
}
setState(() {
_isFirstLoadRunning = false;
});
}
void _loadMore() async {
if (_hasNextPage == true &&
_isFirstLoadRunning == false &&
_isLoadMoreRunning == false) {
setState(() {
_isLoadMoreRunning = true;
});
_page++;
try {
final res = await http.get(
Uri.parse('$_baseUrl?_page=$_page&_limit=$_limit'),
);
log('url : $_baseUrl?_page=$_page&_limit=$_limit');
if (res.statusCode == 200) {
final List fetchedPost = jsonDecode(res.body);
if (fetchedPost.isNotEmpty) {
setState(() {
_posts.addAll(fetchedPost);
});
} else {
setState(() {
_hasNextPage = false;
});
}
}
} catch (e) {
if (kDebugMode) {
log('something went wrong');
}
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
// the controller for listyView
late ScrollController _controller;
#override
void initState() {
super.initState();
_firstLoad();
_controller = ScrollController();
}
#override
void dispose() {
super.dispose();
_controller.removeListener(_loadMore);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.orangeAccent,
elevation: 0.0,
centerTitle: true,
title: Text(
'Paggination',
style: TextStyle(
color: Colors.black.withOpacity(0.52),
fontSize: 20,
),
),
systemOverlayStyle: const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
),
body: _isFirstLoadRunning
? Center(
child: CircularProgressIndicator(
color: Colors.black.withOpacity(0.25),
),
)
: Column(
children: [
Expanded(
child: ListView.builder(
controller: _controller
..addListener(() async {
if (_controller.position.pixels >
_controller.position.maxScrollExtent) {
_loadMore();
}
}),
physics: const BouncingScrollPhysics(),
itemCount: _posts.length,
itemBuilder: (context, index) => Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.065),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.black.withOpacity(0.2),
width: 0.5,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'ID : ${_posts.elementAt(index)['id']}',
),
Text(
'AlbumID : ${_posts.elementAt(index)['albumId']}',
),
Text(
'Title : ${_posts.elementAt(index)['title']}',
textAlign: TextAlign.justify,
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: MediaQuery.of(context).size.width * 0.4,
width: MediaQuery.of(context).size.width * 0.4,
child: Image.network(
_posts.elementAt(index)['url'],
),
),
SizedBox(
height: MediaQuery.of(context).size.width * 0.4,
width: MediaQuery.of(context).size.width * 0.4,
child: Image.network(
_posts.elementAt(index)['thumbnailUrl'],
),
),
],
),
],
),
),
),
),
// when the _loadMore running
if (_isLoadMoreRunning == true)
Container(
color: Colors.transparent,
padding: const EdgeInsets.only(top: 10, bottom: 20),
child: const Center(
child: CircularProgressIndicator(
color: Colors.amberAccent,
strokeWidth: 3,
backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
),
),
),
if (_hasNextPage == false)
SafeArea(
child: Container(
width: double.maxFinite,
padding: const EdgeInsets.only(top: 20, bottom: 20),
color: Colors.orangeAccent,
child: const Text('you get all'),
),
)
],
),
);
}
}

Related

Animation library is giving me this error about lifecycle state

Animation library is giving me this error about lifecycle state-- 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4519 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true. is was getting this error when i used animation controller.
After the error my flutter application crashed and i have to reload everytime.
// ignore_for_file: depend_on_referenced_packages, sized_box_for_whitespace, avoid_print
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:habbit_app/controllers/timer_tab_controller.dart';
import 'package:habbit_app/screens/timer/set_timer_component.dart';
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
import 'package:habbit_app/widgets/text_widget/label_text.dart';
class TimerTab extends StatefulWidget {
const TimerTab({super.key});
#override
State<TimerTab> createState() => _TimerTabState();
}
class _TimerTabState extends State<TimerTab> with TickerProviderStateMixin {
AnimationController? animationController;
TimerTabController tabController =
Get.put(TimerTabController(), permanent: false);
late TabController _controller;
bool isPlaying = false;
// int get countText2 {
// int count = tabController.totalSeconds;
// return;
// }
String get countText {
Duration count =
animationController!.duration! * animationController!.value;
return animationController!.isDismissed
? '${animationController!.duration!.inHours}:${(animationController!.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(animationController!.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void dispose() {
animationController!.dispose();
super.dispose();
}
double progress = 1.0;
void notifiy() {
if (countText == "0:00:00") {
FlutterRingtonePlayer.playNotification();
}
}
#override
void initState() {
// ignore: todo
// TODO: implement initState
TimerTabController tabController =
Get.put(TimerTabController(), permanent: false);
super.initState();
_controller = TabController(length: 3, vsync: this);
_controller.addListener(() {
tabController.tabIndex.value = _controller.index;
// print(tabController.tabIndex.value);
});
super.initState();
animationController = AnimationController(
vsync: this, duration: Duration(seconds: tabController.totalSeconds));
animationController!.addListener(() {
notifiy();
if (animationController!.isAnimating) {
setState(() {
progress = animationController!.value;
});
} else {
setState(() {
progress = 1.0;
isPlaying = false;
});
}
});
}
#override
Widget build(BuildContext context) {
var color = Theme.of(context);
return Obx(
() => tabController.isFirst.value
? SetTimerComponent(
changeAnimation: () {
animationController!.duration =
Duration(seconds: tabController.totalSeconds);
tabController.changeFirst();
},
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// start timer screen
Container(
height: 350,
width: MediaQuery.of(context).size.width,
// color: color.cardColor,
child: Column(children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 300,
height: 300,
child: CircularProgressIndicator(
color: color.primaryColor,
backgroundColor:
color.disabledColor.withOpacity(0.6),
value: progress,
strokeWidth: 10,
)),
GestureDetector(
onTap: () {
if (animationController!.isDismissed) {
showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 300,
child: CupertinoTimerPicker(
initialTimerDuration:
animationController!.duration!,
backgroundColor:
color.backgroundColor,
onTimerDurationChanged: (time) {
setState(() {
animationController!.duration =
time;
});
},
),
));
}
},
child: AnimatedBuilder(
animation: animationController!,
builder: ( context, child) => Text(
countText,
style: TextStyle(
fontSize: 60,
fontWeight: FontWeight.bold,
color: color.primaryColor),
),
),
),
],
),
]),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
print(tabController.totalSeconds);
if (animationController!.isAnimating) {
animationController!.stop();
setState(() {
isPlaying = false;
});
} else {
animationController!.reverse(
from: animationController!.value == 0
? 1.0
: animationController!.value);
setState(() {
isPlaying = true;
});
}
},
child: Container(
height: 35,
width: 90,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(30)),
color: color.primaryColor),
child: Center(
child: LabelText(
text: isPlaying == true ? "PAUSE" : "RESUME",
isBold: true,
isColor: true,
color: Colors.white,
)),
),
),
GestureDetector(
onTap: () {
animationController!.reset();
tabController.currentvalueHour.value = 0;
tabController.currentvalueMin.value = 0;
tabController.currentvalueSec.value = 0;
setState(() {
isPlaying = false;
});
tabController.changeFirst2();
},
child: Container(
height: 35,
width: 90,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(30)),
color: color.disabledColor.withOpacity(0.5)),
child: const Center(
child: LabelText(
text: "DELETE",
isColor: true,
color: Colors.white,
isBold: true,
)),
),
)
],
)
],
),
);
}
}

Pagination in the list does not work when I scroll to the very bottom of the list in flutter

Faced a problem. I started to paginate the list so that 10 elements are displayed, when I reach the bottom of the list using controller.position.extentAfter < 30 I check how far we have gone down and if at the very bottom I change the value of isLoadMoreRunning and show CircularProgressIndicator. I will also add +10 elements to the limit variable for each call to display. I seem to have done everything right, but pagination does not work for me, it shows the first 10 elements, and then nothing passes, new elements are not loaded when I scrolled to the very bottom. What could be the problem?
home
late ScrollController controller;
bool isFirstLoadRunning = false;
bool isLoadMoreRunning = false;
bool hasNextStation = true;
int limit = 10;
void _firstLoadStations() async {
final StationCubit stationCubit = BlocProvider.of<StationCubit>(context);
setState(() {
isFirstLoadRunning = true;
});
try {
stationsList =
await stationCubit.getAllPublicChargingStations(limit: limit);
} catch (error) {
print(error);
}
setState(() {
isFirstLoadRunning = false;
});
}
void _loadMoreStations() async {
final StationCubit stationCubit = BlocProvider.of<StationCubit>(context);
if (hasNextStation == true &&
isFirstLoadRunning == false &&
controller.position.extentAfter < 30) {
setState(() {
isLoadMoreRunning = true;
});
limit += 10;
try {
var fetchedStations =
await stationCubit.getAllPublicChargingStations(limit: limit);
if (fetchedStations.isNotEmpty) {
setState(() {
stationsList.addAll(fetchedStations);
});
} else {
setState(() {
hasNextStation = false;
});
}
} catch (error) {
print(error);
}
setState(() {
isLoadMoreRunning = false;
});
}
// _foundAddressesInStationList = stationsList;
}
#override
void initState() {
_firstLoadStations();
controller = ScrollController()
..addListener(() {
_loadMoreStations();
});
_foundAddresses = _allAddresses;
_foundStation = _allStation;
super.initState();
}
#override
void dispose() {
controller.removeListener(() {
_loadMoreStations();
});
super.dispose();
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
final double paddingTop = MediaQuery.of(context).padding.top;
final StationCubit stationCubit = BlocProvider.of<StationCubit>(context);
return Container(
width: size.width,
height: size.height,
child: _child(size, paddingTop, stationCubit),
);
}
Widget _child(Size size, double paddingTop, StationCubit stationCubit) =>
BlocBuilder<StationCubit, StationState>(
builder: (context, state) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
children: [
const SizedBox(height: 17),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
_addresses(size, stationCubit),
],
),
),
),
],
),
),
);
Widget _addresses(Size size, StationCubit stationCubit) => ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height / 2,
),
child: SizedBox(
width: size.width,
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
isFirstLoadRunning
? const CircularProgressIndicator(
color: Colors.white)
: const Text(
'Addresses',
style: constants.Styles.smallBookTextStyleWhite,
),
const SizedBox(height: 25),
ListViewSearch(
stationList: stationsList,
controller: controller,
),
const SizedBox(height: 20),
if (isLoadMoreRunning == true)
const Padding(
padding: EdgeInsets.only(top: 10, bottom: 40),
child: Center(
child: CircularProgressIndicator(),
),
),
if (hasNextStation == false)
Container(
padding: const EdgeInsets.only(top: 30, bottom: 40),
color: Colors.amber,
child: const Center(
child:
Text('You have fetched all of the content'),
),
),
],
),
)),
),
),
),
);
}
cubit
Future<List<PublicChargingStationModel>> getAllPublicChargingStations(
{required int limit}) async {
var result =
await StationRepository().getAllPublicChargingStations(limit: limit);
return result;
}
repository
Future<List<PublicChargingStationModel>> getAllPublicChargingStations(
{int? limit}) async {
try {
var apiKeyMap = await ApiKey.getCryptoApiKeyMap();
apiKeyMap!.addAll({'limit': limit.toString()});
final Uri url = Uri.parse(
'${ApiConfig.schema}://${ApiConfig.domain}/${ApiConfig.uriPrefix}/stations',
).replace(queryParameters: apiKeyMap);
final response = await http.get(url);
if (response.statusCode == 200) {
final data = jsonDecode(response.body)['data'] as List;
return data
.map((json) => PublicChargingStationModel.fromJson(json))
.toList();
}
return List<PublicChargingStationModel>.empty();
} catch (_) {
print(_);
return List<PublicChargingStationModel>.empty();
}
}
you need set SingleChildScrollView parameter controller: controller.

FLUTTER : How to only rebuild the top page in stack flutter

I have an issue with rebuilding pages in the stack with Flutter
This is all my users and have added the search to the top appBar.
But it works with conditions to see if it is widgets there then get height of the widgets that is being fixed underneath the appBar...But that happens asynchronously.
So when firstLoad it works but when I call setState it then rebuilds all the pages in the stack and with that it looks like this
This is how it looks after a set state. The problem i saw is that the previous pages have an influence. I couldn't find a good viable solution to this. Will explain my architecture.
I have a page Layout that is a container wrapper for all my pages that has it's appBar styles and just sends through the children. But that is the page Layout wrapper that is being rebuild every time the a set States happen
HOW I GET MY WIDGET SIZE
HOW I IMPLEMENTED IT
It goes in the else with the other pages in the stack.. I tried putting it in the initState but it never goes inside because it is used in the other pages in the stack
I only need an implementation to rebuild the TOP page in the stack.
PAGE LAYOUT
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutterweb/constants.dart';
import 'package:flutterweb/controllers/channel_controller.dart';
import 'package:flutterweb/controllers/user_controller.dart';
import 'package:flutterweb/main.dart';
import 'package:flutterweb/models/channels_model.dart';
import 'package:flutterweb/utils/functions.dart';
import 'package:flutterweb/views/channels/func.dart';
import 'package:flutterweb/views/home/home.dart';
import 'package:flutterweb/views/menu/permissions/choose_assign_group.dart';
import 'package:flutterweb/widgets/builders/KNetworkFadeImage.dart';
import 'package:flutterweb/widgets/builders/kPopups.dart';
import 'package:flutterweb/widgets/drawerDara.dart';
import 'package:get/get.dart';
class CustomAppBar extends StatefulWidget {
final String title;
final List<Map<String, dynamic>>? topTabs;
final TabController? topTabController;
final List<Widget>? children;
final List<Widget>? childrenFixed;
final Function? leftActionFunction;
final Icon? leftActionIcon;
final Drawer? drawer;
final Function? logOutPressed;
final bool showOptionsMenu;
final Widget? optionMenu;
final Widget? bottomNavigationBar;
final String? backGroundImage;
final ScrollController? scrollController;
CustomAppBar({
required this.title,
this.topTabs,
this.topTabController,
this.leftActionFunction,
this.leftActionIcon,
this.children,
this.childrenFixed,
this.drawer,
this.logOutPressed,
this.showOptionsMenu = false,
this.optionMenu,
this.bottomNavigationBar,
this.backGroundImage,
this.scrollController,
});
#override
_CustomAppBarState createState() => _CustomAppBarState();
}
double app_content_height = 0;
double fixedWidgetSize = 0;
String prevTitle = "";
class _CustomAppBarState extends State<CustomAppBar>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> scaffoldkey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
_toggleAnimation() {
scaffoldkey.currentState!.openDrawer();
}
double _getAppBarSize() {
double fixedHeightInclude = fixedWidgetSize;
if (widget.topTabs != null) {
fixedHeightInclude += 100;
} else if (widget.title == "") {
fixedHeightInclude += 0;
} else {
fixedHeightInclude += 60;
}
return fixedHeightInclude;
}
#override
Widget build(BuildContext context) {
// if (widget.title != global_title) return SizedBox();
List<Widget> arr = [];
Widget arrView = SizedBox();
double statusBar = 0;
double _width = MediaQuery.of(context).size.width;
Widget? fixedChild = SizedBox();
if ((widget.childrenFixed?.length ?? 0) > 1) {
fixedChild = WidgetSize(
child: Column(children: widget.childrenFixed!),
onChange: (Size size) {
fixedWidgetSize = 0;
setState(() {
fixedWidgetSize = size.height;
});
kPrint("fixedWidgetSize ${size.height}");
},
);
} else {
fixedWidgetSize = 0;
}
// Widget? fixedChild = (widget.childrenFixed?.length ?? 0) > 1
// ? WidgetSize(
// child: Column(children: widget.childrenFixed!),
// onChange: (Size size) {
// fixedWidgetSize = 0;
// setState(() {
// fixedWidgetSize = size.height;
// });
// kPrint("fixedWidgetSize ${size.height}");
// },
// )
// : SizedBox();
app_content_height =
MediaQuery.of(context).size.height - _getAppBarSize() - statusBar;
if (widget.title != "") {
arr.add(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: widget.showOptionsMenu == true
? IconButton(
icon: const Icon(Icons.menu, color: Colors.white),
onPressed: () => _toggleAnimation(),
)
: IconButton(
icon: widget.leftActionIcon ??
const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
if (widget.leftActionFunction != null) {
widget.leftActionFunction!();
} else {
if (Navigator.canPop(context)) {
Get.back();
}
}
},
),
),
Expanded(flex: 2, child: SizedBox()),
Expanded(
flex: 10,
child: Center(
child: Text(
widget.title,
style: const TextStyle(color: Colors.white, fontSize: 24.0),
),
),
),
Expanded(
flex: 4,
child: widget.logOutPressed != null
? IconButton(
icon: const Icon(Icons.power_settings_new_outlined,
color: Colors.white),
onPressed: () {
widget.logOutPressed!();
},
)
: widget.optionMenu ?? Container(),
),
],
),
);
}
if (widget.topTabs != null) {
List<Widget> tempTopBar = [];
List<Widget> tempTopView = [];
for (var i = 0; i < widget.topTabs!.length; i++) {
String key = widget.topTabs![i].keys
.toString()
.replaceAll("(", "")
.replaceAll(")", "");
Widget value = widget.topTabs![i][key];
tempTopBar.add(Tab(text: key));
tempTopView.add(SingleChildScrollView(child: value));
}
arr.add(
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Center(
child: Container(
height: 30,
child: DefaultTabController(
length: widget.topTabs!.length,
child: TabBar(
labelPadding: widget.topTabs!.length == 2
? const EdgeInsets.symmetric(horizontal: 40.0)
: const EdgeInsets.symmetric(horizontal: 16.0),
controller: widget.topTabController,
indicatorSize: TabBarIndicatorSize.tab,
indicator: CircleTabIndicator(color: Colors.white, radius: 4),
isScrollable: true,
labelColor: Colors.white,
tabs: tempTopBar,
),
),
),
),
),
);
// arr.add(child);
arrView = Container(
width: _width,
height: app_content_height,
child: TabBarView(
controller: widget.topTabController,
children: tempTopView,
),
);
}
if (widget.children != null) {
arrView = Container(
width: _width,
height: app_content_height,
child: ListView(
// controller: widget.scrollController ?? ScrollController(),
children: widget.children!,
),
);
}
_getStatus() {
if (statusBar > 0) {
Color color = AppColors.kBlue;
return Container(
height: Get.height * 0.03,
width: Get.width,
color: color,
child: const Center(
child: Text(
"",
style: const TextStyle(color: Colors.black),
),
),
);
} else {
return const SizedBox();
}
}
return SafeArea(
child: Material(
child: Stack(
children: [
Scaffold(
resizeToAvoidBottomInset:
true, //That the keyboard shows correctly
extendBodyBehindAppBar: true,
key: scaffoldkey,
appBar: PreferredSize(
preferredSize: Size.fromHeight(
_getAppBarSize()), // here the desired height
child: Container(
decoration: kAppBarBoxDecorations,
child: Column(
children: [
Column(
children: arr,
),
fixedChild,
],
),
),
),
drawer: Drawer(
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: InkWell(
onTap: () {
Channels element =
ChannelController.to.gSelectedChannel.value;
getChannelRoles(element);
},
child: Text(
"${ChannelController.to.gSelectedChannel.value.rolDesc} >",
),
),
accountEmail: Text(
UserController.to.gUserModel.value.email.toString()),
currentAccountPicture: GestureDetector(
child: const CircleAvatar(
backgroundImage: NetworkImage(
"https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"),
),
onTap: () => print("Current User")),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(
"${URLS.keyBaseUrl}/assets/images/background/background7.jpg")),
),
),
ListTile(
title: const Text("Home"),
trailing: const Icon(Icons.home),
onTap: () =>
Get.toNamed(Home.router, preventDuplicates: false),
),
ListTile(
title: const Text("Menu Permissions"),
trailing: const Icon(Icons.home),
onTap: () => Get.toNamed(ChooseAssignGroup.router,
preventDuplicates: false),
),
const Divider(
thickness: 1.0,
),
drawerData(),
const Divider(
thickness: 1.0,
),
ListTile(
title: const Text("Close"),
trailing: const Icon(Icons.cancel),
onTap: () => Navigator.of(context).pop(),
),
ListTile(
title: const Text("Log Out"),
trailing: const Icon(Icons.logout),
onTap: () => UserController.to.logOutUser(),
),
],
),
),
body: Container(
decoration: widget.backGroundImage != null
? BoxDecoration(
color: Colors.black.withOpacity(0.9),
image: DecorationImage(
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.2), BlendMode.dstATop),
image: NetworkImage(widget.backGroundImage!),
),
)
: BoxDecoration(color: Colors.grey.shade400),
child: Center(
child: Container(
constraints: BoxConstraints(maxWidth: 800),
padding: EdgeInsets.only(
left: 15.0, right: 15.0, top: _getAppBarSize()),
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: arrView,
),
),
),
),
bottomNavigationBar: widget.bottomNavigationBar,
),
],
),
),
);
}
}
class CircleTabIndicator extends Decoration {
final BoxPainter _painter;
CircleTabIndicator({required Color color, required double radius})
: _painter = _CirclePainter(color, radius);
#override
BoxPainter createBoxPainter([onChanged()?]) => _painter;
}
class _CirclePainter extends BoxPainter {
final Paint _paint;
final double radius;
_CirclePainter(Color color, this.radius)
: _paint = Paint()
..color = color
..isAntiAlias = true;
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
final Offset circleOffset =
offset + Offset(cfg.size!.width / 2, cfg.size!.height - radius);
canvas.drawCircle(circleOffset, radius, _paint);
}
}
class WidgetSize extends StatefulWidget {
final Widget child;
final Function onChange;
const WidgetSize({
required this.onChange,
required this.child,
});
#override
_WidgetSizeState createState() => _WidgetSizeState();
}
class _WidgetSizeState extends State<WidgetSize> {
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
ALL VERIFIED USERS
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutterweb/controllers/channel_controller.dart';
import 'package:flutterweb/controllers/user_controller.dart';
import 'package:flutterweb/main.dart';
import 'package:flutterweb/models/user_model.dart';
import 'package:flutterweb/thirdParty/googleSignin.dart';
import 'package:flutterweb/utils/functions.dart';
import 'package:flutterweb/views/menu/permissions/menu_assign.dart';
import 'package:flutterweb/widgets/builders/kPopups.dart';
import 'package:flutterweb/widgets/buttons/KIconOnlyButton.dart';
import 'package:flutterweb/widgets/builders/KNetworkFadeImage.dart';
import 'package:flutterweb/widgets/builders/customAppBar.dart';
import 'package:flutterweb/widgets/buttons/KButton.dart';
import 'package:flutterweb/constants.dart';
import 'package:flutterweb/widgets/cards/KStudentInfoCard.dart';
import 'package:flutterweb/widgets/cards/kStudentCard.dart';
import 'package:flutterweb/widgets/input/KInputBar.dart';
import 'package:flutterweb/widgets/input/KTextField.dart';
import 'package:flutterweb/widgets/static/kLabel.dart';
import 'package:flutterweb/widgets/text/kInfo.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
class AllVerifiedUsers extends StatefulWidget {
static const String router = "/allVerifiedUsers";
AllVerifiedUsers({Key? key}) : super(key: key);
#override
_MyPageState createState() => _MyPageState();
}
// The controller for the ListView
late ScrollController _controller;
class _MyPageState extends State<AllVerifiedUsers> {
// The controller for the ListView
late ScrollController _controllerTest;
int _page = 1;
final int _limit = 20;
bool _hasNextPage = true;
bool _isFirstLoadRunning = false;
bool _isLoadMoreRunning = false;
List<UserModel> _posts = [];
String searchVal = "";
void _firstLoad() async {
setState(() {
_isFirstLoadRunning = true;
});
try {
List<UserModel> lUserMode = await ChannelController.to
.fetchUsersChannels(_limit, _page, searchVal);
setState(() {
_posts = lUserMode;
});
} catch (err) {
kPrint('Something went wrong');
}
setState(() {
_isFirstLoadRunning = false;
});
}
void _loadMore() async {
if (_isFirstLoadRunning == false &&
_isLoadMoreRunning == false &&
_controller.position.extentAfter < 300) {
setState(() {
_isLoadMoreRunning = true; // Display a progress indicator at the bottom
});
_page += 1; // Increase _page by 1
try {
List<UserModel> lUserMode = await ChannelController.to
.fetchUsersChannels(_limit, _page, searchVal);
if (lUserMode.isNotEmpty) {
setState(() {
_hasNextPage = true;
_posts.addAll(lUserMode);
});
} else {
// This means there is no more data
// and therefore, we will not send another GET request
setState(() {
_hasNextPage = false;
});
}
} catch (err) {
print('Something went wrong!');
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
#override
void initState() {
super.initState();
_firstLoad();
_controller = ScrollController()..addListener(_loadMore);
_controllerTest = ScrollController()
..addListener(() => {kPrint("CustomView Scroll")});
}
#override
void dispose() {
super.dispose();
_controller.removeListener(_loadMore);
}
#override
Widget build(BuildContext context) {
global_title = "All Verified Users";
return CustomAppBar(
title: "All Verified Users",
scrollController: _controllerTest,
childrenFixed: [
kAddSpace(2),
CustomInputBar(
inverse: true,
title: "Search",
onChanged: (String value) {
if (value == "") {
_firstLoad();
return;
}
setState(() {
searchVal = value;
_posts = [];
});
_loadMore();
},
),
],
children: [
kAddSpace(2),
KLabel(
label: "Choose Verified User",
),
kAddSpace(2),
_isFirstLoadRunning
? const Center(
child: CircularProgressIndicator(),
)
: Column(
children: [
SizedBox(
height: app_content_height,
// width: Get.width,
child: ListView.builder(
shrinkWrap: true,
controller: _controller,
itemCount: _posts.length,
itemBuilder: (_, index) {
UserModel item = _posts[index];
return KStudentCard(
imgUrl: "",
onPressed: () {
ChannelController.to.gSelectedMenuUserModel.value =
item;
Get.toNamed(MenuAssign.router);
},
name: "${item.name} ${item.surname}",
);
},
),
),
// when the _loadMore function is running
if (_isLoadMoreRunning == true)
const Padding(
padding: EdgeInsets.only(top: 10, bottom: 40),
child: Center(
child: CircularProgressIndicator(),
),
),
// When nothing else to load
if (_hasNextPage == false)
Container(
padding: const EdgeInsets.only(top: 30, bottom: 40),
color: Colors.amber,
child: const Center(
child: Text('You have fetched all of the content'),
),
),
],
),
kAddSpace(2),
],
);
}
}
Thank you
As I understand it, the idea is a fixed TextArea and a scrollable List beneath. A solution that works without computing any height would be:
final items = List<String>.generate(1000, (i) => 'Item $i');
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
//
// TEXTBOX
//
Container(
color: Colors.red,
child: Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
// controller: controller,
))),
const SizedBox(height: 16),
//
// LIST
//
Expanded(
child: ListView.builder(
shrinkWrap: true, // IMPORTANT
itemCount: items.length,
itemBuilder: (context, index) {
return Text(items[index]);
})),
]),
);
}

I want to refresh a ListView when search item is selected from dropdown

I have a screen as soon as i navigate to this screen from previous screen i fetchs data from server according to selected location. For this i call getData() method in initState() method.
On this Screen i have a searchbar on top which shows dropdown list of locations, and below i have a listview which shows data according to choosen location.
Now I want to refresh listView when a user selects any location from dropdown list from search bar. I'm stuck how can i call my getData() method again when selected loation changed.
.
class SearchResultPage extends StatefulWidget {
final TypeAheadModel selectedLocation;
SearchResultPage(this.selectedLocation);
#override
_SearchResultPageState createState() => _SearchResultPageState();
}
class _SearchResultPageState extends State<SearchResultPage>
with AutomaticKeepAliveClientMixin<SearchResultPage> {
late ScrollController _scrollController;
late FoodSearchViewModel viewModel;
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(_scrollListener);
viewModel = FoodSearchViewModel();
getData(widget.selectedLocation);
}
#override
void didUpdateWidget(SearchResultPage oldWidget) {
super.didUpdateWidget(oldWidget);
}
Future<List<DishModel>> getData(TypeAheadModel typeAheadModel) async {
if (!viewModel.isLoading) {
setState(() {
viewModel.isLoading = true;
});
var result = await viewModel.getData(widget.selectedLocation);
setState(() {
if (result.isEmpty || result.length == viewModel.totalCount) {
viewModel.hasLoadMore = false;
}
viewModel.isLoading = false;
viewModel.showShimmer = false;
});
}
return viewModel.foodList;
}
void _scrollListener() {
loadMore();
}
Future<List<DishModel>> loadMore() async {
if (!viewModel.isLoading) {
if (_scrollController.position.extentAfter < 150) {
setState(() {
viewModel.isLoading = true;
});
var moreItems = await viewModel.loadMore(widget.selectedLocation);
setState(() {
if (moreItems.isEmpty ||
viewModel.foodList.length == viewModel.totalCount) {
viewModel.hasLoadMore = false;
}
viewModel.isLoading = false;
});
}
return viewModel.foodList;
}
return viewModel.foodList;
}
final brandId = 0;
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (Navigator.of(context).userGestureInProgress)
return false;
else
return true;
},
child: Scaffold(
backgroundColor: kBackgroundColor,
body: Container(
child: ListView.builder(
itemCount: viewModel.showShimmer
? 5
: (viewModel.hasLoadMore
? viewModel.foodList.length + 1
: viewModel.foodList.length),
controller: _scrollController,
cacheExtent: 9999,
itemBuilder: (context, index) {
print(index);
if (!viewModel.showShimmer &&
viewModel.hasLoadMore &&
index >= viewModel.foodList.length) {
return Padding(
padding: EdgeInsets.all(5),
child: const Center(child: CupertinoActivityIndicator()),
);
} else {
if (viewModel.showShimmer) {
return buildShimmer();
} else {
return DishTile(
dishNameTextStyle: titleTextStyle,
textStyle: subtitleTextStyle,
dish: viewModel.foodList[index]);
}
}
},
),
),
),
);
}
Widget buildShimmer() => Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
width: double.infinity,
height: 140,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
child: Row(
children: [
ShimmerWidget.rectangular(
height: double.infinity,
width: MediaQuery.of(context).size.width * .45,
),
Flexible(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(top: 8, left: 8, right: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerWidget.rectangular(height: 10),
SizedBox(
height: 10,
),
ShimmerWidget.rectangular(
height: 8,
width: MediaQuery.of(context).size.width * .30,
),
SizedBox(
height: 6,
),
ShimmerWidget.rectangular(
height: 8,
width: MediaQuery.of(context).size.width * .20,
),
],
),
),
),
)
],
),
);
#override
bool get wantKeepAlive => true;
}
It's quite hard to give any concrete example since your code is not executable but the basic idea is: use didUpdateWidget:
#override
void didUpdateWidget(SearchResultPage oldWidget) {
super.didUpdateWidget(oldWidget);
getData(widget.selectedLocation);
}
I see you already had didUpdateWidget so maybe you had an error trying to do this? If so please share the error.

setState() or markNeedsBuild() called during build on ListView

So I am trying to refactor my listView logic. Basically my ListView has become cumbersome with the UI logic , so I decided, why not move certain parts of the UI logic to another class
This is my code
ListPage.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/ListTextArea.dart';
import 'package:sample_flutter_works/Model.dart';
import 'dart:convert';
import 'package:sample_flutter_works/RefreshTableContainer.dart';
class ListPage extends StatefulWidget {
#override
MyListPage createState() => MyListPage();
}
class MyListPage extends State<ListPage> {
MessageList messageList;
List<int> viewTimeInfo;
ScrollController _controller;
_scrollListener() {
}
#override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
_controller = ScrollController();
_controller.addListener(_scrollListener);
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
void loadMessages({completionBlock}) async {
var jsonString = await rootBundle.loadString('assets/Chat.json');
final jsonResponse = json.decode(jsonString);
if (jsonResponse != null) {
completionBlock(MessageList.fromJSON(jsonResponse));
} else {
completionBlock(null);
}
}
Widget listLayout() {
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: (messageList != null && messageList.msgList != null)
? messageList.msgList.length
: 0,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 4.0,
),
itemBuilder: (BuildContext context, int index) {
var msgValToSend =
(messageList != null && messageList.msgList != null)
? messageList.msgList[index]
: null;
return Stack(
children: <Widget>[
IntrinsicHeight(
child: Row(
children: <Widget>[
getTheImageLayout(msgValToSend),
new ListTextArea(
msg: msgValToSend,
didTapOnTextArea: tappedOnTextArea,
visibilityCheck: checkForVisibility)
],
),
)
],
);
});
}
tappedOnTextArea(Message msg) {
var viewedInfo = this.viewTimeInfo;
if (viewedInfo != null) {
var indexOfTappedElement = viewedInfo.indexOf(msg.messageID);
if (indexOfTappedElement != null && indexOfTappedElement != -1) {
viewedInfo.removeAt(indexOfTappedElement);
} else {
viewedInfo.add(msg.messageID);
}
} else {
viewedInfo = [msg.messageID];
}
setState(() {
viewTimeInfo = viewedInfo;
});
}
checkForVisibility(bool _visible, Message msg) {
if (msg != null && this.viewTimeInfo != null) {
var checkForIndex = this.viewTimeInfo.indexOf(msg.messageID);
if (checkForIndex != null && checkForIndex != -1) {
_visible = true;
}
}
}
Widget getTheImageLayout(Message msg) {
return Expanded(
flex: 2,
child: Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 2.5, 0, 0),
child: Container(
color: Colors.red,
height: 50,
child: Row(
children: <Widget>[
userImageView(msg),
],
)),
)));
}
Widget userImageView(Message msg) {
return Expanded(
flex: 8,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: 40.0,
height: 40.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: ClipOval(
child: Image.network(
(msg.msgUser.userPicUrl != null)
? msg.msgUser.userPicUrl
: 'https://picsum.photos/250?image=9',
fit: BoxFit.fill,
),
))));
}
Future<void> refreshTheChatTable() async {
print(" This is where the logic of pulll 2 refresh must be written ");
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
#override
Widget build(BuildContext context) {
return new RefreshTableContainer(
listLayout: listLayout(),
pull2RefreshAction: refreshTheChatTable,
);
}
}
ListTextArea.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/Model.dart';
class ListTextArea extends StatelessWidget {
Message msg;
Function didTapOnTextArea;
Function visibilityCheck;
ListTextArea({
this.msg,
this.didTapOnTextArea,
this.visibilityCheck
});
#override
Widget build(BuildContext context) {
return Expanded(
flex: 8,
child: GestureDetector(
onTap: didTapOnTextArea(msg),
child: Padding(
padding: EdgeInsets.fromLTRB(0, 2.5, 10, 0),
child: Column(
children: getChildWidgetArray(msg) ,
),
),
));
}
List<Widget> getChildWidgetArray(Message msg) {
var elementalArray = [
Align(
alignment: Alignment.topLeft,
child: Text(
(msg != null) ? msg.msgContent.content : "Data Loading",
style: TextStyle(
background: Paint()..color = Colors.orange,
),
),
),
Spacer(), // Defaults to a flex of one.
Align(
alignment: Alignment.bottomRight,
child: Text(
'Date of sending',
textDirection: TextDirection.rtl,
style: TextStyle(
background: Paint()..color = Colors.blue,
),
),
)
];
var _visible = false;
visibilityCheck(_visible,msg);
var timeInfo = AnimatedOpacity (
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Align(
child: _visible ? (Align(alignment: Alignment.topLeft,child:Column(children: <Widget>[Text("Last Read :" + (msg.msgTimeInfo.lastReadInfo)),
Text("Delievered :" + (msg.msgTimeInfo.deliveredInfo))],))): null));
elementalArray.add(timeInfo);
return elementalArray;
}
}
The error is as follows:
What I am trying to do ( or had done earlier on when the entire code was in ListPage.dart ) was dynamically calculated cells in a listView, each cell responding to a tap action that shows in more data. I don't understand what I did wrong here at all.
I called the setState in init but inside a callback function. The statelesswidget ListTextArea will not handle the state at all, but returns the tapAction to the StateFulWidget ListPage.dart.
So why am I getting this error. Any insights would be helpful.
In my case, the error occurred when I was setting the state before build was complete, so, I deferred it to the next tick and it worked.
previously
myFunction()
New
Future.delayed(Duration.zero, () async {
myFunction();
});
The problem is in ListTextArea.dart, line
onTap: didTapOnTextArea(msg),
You are calling function didTapOnTextArea in build method instead of passing it as tap listener. You have to replace it with
onTap: (){
didTapOnTextArea(msg);
},