I need to update my page view, the data will come from the database(Firestore). refresh the page view and bring the data
actually, I need to develop an app in which users will post an image and description and other users will be able to view those...
List<Widget> stories = [Story_Template(),Story_Page(),Story_Template()]; // ignore: camel_case_types class
Story_Newsfeed_State extends State<Story_Newsfeed> {
#override Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: PageView.builder(
itemBuilder : (context,position) => stories[position],
itemCount: stories.length,
scrollDirection: Axis.vertical,
));
}
}
I have created a similar motivational wallpaper App in flutter using the flutter_staggered_grid_view instead of the pageview
You may please refer the project. Hope it helps
Source code GitHub
Related
After Flutter 3.0 the ListView(reverse: true) in my project has changed behaviour. In older version it can be refreshed from bottom pull but now it doesn't, it can just refresh from top.
I know pull_to_refresh package but I am looking for a core solution like old days.. Github issues indicate that It is intended but I don't wanna reverse flutter version.
Does anybody have idea on flutter 3 and without extra packages?
RefreshIndicator(
key: refreshKey,
onRefresh: () async { onRefresh(); },
child: buildMessagesView(messages),
),
ListView buildMessagesView(List<MessageModel> messages) {
return ListView.builder(
scrollDirection: Axis.vertical,
reverse: true,
itemCount: messages.length,
itemBuilder: (_, index) {
return Message(message: messages.elementAt(index));
},
);
}
while ListView reverse:true I expected the Refresh Indicator work by pull from down but it doesn't..
UPDATE
Solved. The ScrollController answer was very useful for me and I developed a simple widget that wraps the ListView to integrate it into the project. You can find the code here.
https://github.com/bnurd/reversed_listview_refresh
I saw the github issue. Actually I realized this behaviour without refresh indicator widget. You can use ScrollController. Create ScrollController instance with listener, attach it to ListView. Write code inside listener that check whether scroll ended. This is listener function sample code:
void _onScroll() {
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.position.pixels;
if (maxScroll - currentScroll == 0) {'YOUR REFRESH CODE'}
}
This is how you attach this listener to ScrollController:
late final _scrollController = ScrollController()..addListener(_onScroll);
IMPORTANT: Dont' forget to attach scrollController to ListView.
I have 3 views which are accessible via the bottom navigation tab. Each view has its own ListView, which looks like this:
// primary = bottomTabNavigation.index //
ListView(
controller: primary ? null : scrollController,
key: const PageStorageKey<String>('view1'),
primary: primary,
physics: primary
? AlwaysScrollableScrollPhysics()
: NeverScrollableScrollPhysics(),
children: const [
Text("A"),
SizedBox(height: 1000),
Text("B"),
],
),
If I start a big swipe on view1, and switch to view2 via bottom tab navigator, the scroll position when I come back to view1 is still at the top. Somehow, the scroll position only saves upon the scrolling animation completing.
Is there some way to switch tabs and store the last position (without waiting for animation)?
Create a Key outside the build method
final _key = GlobalKey();
Step 1: make your widgets staefulWidget.
Step 2: now use AutomaticKeepAliveClientMixin using with keyword.
class _DealListState extends
State<DealList> with
AutomaticKeepAliveClientMixin<DealList>
{
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
// your current widget build
}}
It will keep your listview and other states when you moves from one page to another.
Note: if it's impossible to change every page to stateful widget then just make a new StatefulWidget that use AutomaticKeepAliveClientMixin and will take a child widget from outside and now you can use this widget to wrap your already present widget and can be used through the app.
I use auto_route package for web project to test Navigation 2.0. I use AutoRoureWrapper interface to wrap each page with some common PageScaffold widget, i.e.
class SomePage extends StatefulWidget implements AutoRoureWrapper {
static const title = '/some-page';
...
Widget wrappedRoute(BuildContext context) {
return PageScaffold(
title: title,
body: this,
);
}
...
}
class PageScaffold ... {
}
class _PageScaffoldState ... {
...
#override
Widget build(...) {
return FutureBuilder<User>(
future: futureToGetSidebarItems(),
builder: (context, snapshot) {
if(snapshot.hasData) {
final items = snapshot.data;
final initialRoute = _getInitialRoute(items);
return Row(
children: [
Sidebar(items: items, initialRoute: initialRoute),
const VerticalDivider(width: 1),
Expanded(child: widget.body),
],
);
},
},
);
}
}
I have a Sidebar widget which contains list of clickable links to switch to route, using
AutoRoute.of(context).pushNamed('<route name>');
Now I have a problem when I switch to new page (set new route). Pushing new route forces refresh of all previously (pushed) created instances of PageScaffold and Sidebar widget receives incorrect data. I.e.
Push Dashboard page (after authentication). [PageScaffold(body: Dashboard())] is in history. Network request for user permissions is executed.
Push FirstPage page. [PageScaffold(body: Dashboard()), PageScaffold(body: FirstPage())] is in history. 2 network requests for user permissions (pear each page) are executed.
Push SecondPage page. [PageScaffold(body: Dashboard()), PageScaffold(body: FirstPage()),, PageScaffold(body: SecondPage())] is in history. 3 network requests for user permissions (pear each page) are executed.
And so on...
Why so? Why all pushed pages are refreshed and not only last one? Or what I do wrong?
P.S. When I use default Navigation 1.0 it looks good. Yes, all pushed pages accumulates in history but only latest on is refreshed.
I have a list of items (5-6 items) that are displayed using a ListView.builder. Each item contains a DropdownButton widget for the user to pick a number from 1 - 1000, thus containing 1000 DropdownMenuItems.
I implemented it as shown below, but the problem is that scrolling down the ListView is too slow and stutters. Even if the listView has 5 or 6 items, but note that each of them has an embedded DropdownButton containing 1000 DropdownMenuItems.
Is there a fix? Or another way to achieve my requirement?
N.B: Even if I reduce the number of DropdownMenuItems to 100, it still stutters when scrolling down the ListView.
class List extends StatelessWidget {
final List<Item> // Contains 5 items.
final List<int> quantityList = List<int>.generate(1000, (int i) => i);
//--
child: ListView.builder(
itemBuilder: (buildContext, i) {
return MyItem(
quantityList,
);
},
itemCount: items.length(),
)
class MyItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: DropdownButton<int>(
items: quantityList
.map(
(int e) =>
DropdownMenuItem<int>(
value: e,
child: Text(e.toString()),
),
)
.toList(),
),
),
);
}
Edit
I changed MyItem class to be as below, but still, the same problem exists.
Tried using ListView and ListView.custom instead of ListView.builder, to build the whole list in advance instead of lazily according to this, but still same issue.
I also tried running the app using --profile configuration to simulate a release version. The performance is better but still suffers from terrible stuttering and lag. Tested on emulator and physical device.
class MyItem extends StatelessWidget {
List<DropDownMenuItem> quantityList; // contains 1k
#override
Widget build(BuildContext context) {
return Container(
width:300,
height:300,
child: DropdownButton<int>(
items: quantityList,
),
),
);
}
ListView will create and destroy widgets as they come in and out of view. Your MyItem widget is a very heavy widget (lots of stuff to create and destroy).
You probably don't want each MyItem to be a Scaffold(). Normally you only have 1 Scaffold() visible as it's a fancy root view for an app. It has an app bar, snack bar, drawer, etc. Try having just your Container(...) that's currently under body: as the return from your MyItem.build().
In the items: of your DropdownButton, you build and destroy the list of items when the DropdownButton scrolls in and out of view. If this list is the same for every widget in your list like in your test code above, create one List<Widget>[] that contains your DropdownMenuItem widgets and pass it in to your MyItem() widgets. Something like:
//In your widget with the ListView
List<Widget> myItems;
//In the initState() of your widget with the ListView
...
myItems = quantitySelection.map(
(int e) => DropdownMenuItem<int>(
value: e,
child: Text(e.toString()),
),
).toList(),
...
//In your ListView.builder()
return MyItem(
...
items: myItems,
...
);
//In your MyItem.build() -> DropdownButton()
...
DropDownButton(
items: items
),
...
FWIW - we have a ListView with complex children that we test with 10,000 items. There's a significant difference in performance between the debug and release builds in Flutter. It stutters a little in the debug builds but is very smooth in the release builds.
I was able to solve the issue by only using the cacheExtent property of ListView.builder, setting it to list.length*200. This is kind of a workaround knowing that in my case the list length will always be small.
Pre-building the DropDownMenuItems had no sensed performance enhancement by a user, but it is a good recommended practice regardless, as instead of building the same DropDownMenuItems over and over again for every list item.
Although according to the docs: ListView and ListView.separated does not lazy load items rather build them all up at the beginning, I kept experiencing the same stuttering and lag during scrolling as with ListView.builder.
I would like to find a way to update the physics param to disable the swipe action in a parent PageView from a child widget.
I am using riverpod for updating the state in a child widget when it builds to know when I should pass NeverScrollableScrollPhysics to the physics param in a parent widget. But the thing is that this approach is causing my child widget to rebuild recursively, because this is making the PageView rebuild to update the physics param. So I really don't know what to do here.
I have this parent Widget that builds the PageView:
Widget build(BuildContext context) {
final _navBar = useProvider(bottomNavigationBarProvider);
return PageView(
physics: navBar.isSwipeBlocked ? const NeverScrollableScrollPhysics() : null,
controller: pageController,
onPageChanged: onPageChanged,
children: [
Beamer(
key: const Key('feed-tab'),
routerDelegate: BeamerDelegate(locationBuilder: (state) => FeedLocation(state)),
),
]
)
}
And the child Widget that updates the state variable:
Widget build(BuildContext context) {
final _navBar = useProvider(bottomNavigationBarProvider.notifier);
useEffect(() {
Future.microtask(() => _navBar.blockSwipe());
}, []);
return Container(...);
}
So when FeedLocation loads, it updates _navBar in an attempt for disabling the scroll behavior. But as I mentioned, this causes the parent to rebuild and FeedLocation to build again and then the recursive state..
The idea was to be able to go to FeedLocation, disable the scroll, then when go back, enable it again, but I don't see a solution for that.
I think I did already what this guy suggested https://github.com/flutter/flutter/issues/37510#issuecomment-738051469 using Riverpod
And I guess I am a similar situation as this guys from the same thread https://github.com/flutter/flutter/issues/37510#issuecomment-864416592
Is anybody able to see a solution or what I am doing wrong?
You should replace null as the secondary ScrollPhysics with PageScrollPhysics() and make sure to add setState(() {}); when you update the isSwipeBlocked variable.