PageStorageKey not loading between navigation - flutter

I've looked at just about every piece of info I can find on this subject but none of the solutions seem to be working for me. I have a persistent top nav bar that I pass the views through using a the MaterialApp builder function as shown below. Inside that NavFrame class I simply have two buttons to switch between views. The second view has a GridView with a PageStorageKey attached to it. If you scroll and switch between views you'll see the scroll position is not loaded when going back to the grid in view 2. I'm using the latest Auto-Route package for generating my routes.
main.dart
void main() {
runApp(MyApp());
}
final _navKey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageStorageKey Debugging',
theme: ThemeData(
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: Routes.view1,
navigatorKey: _navKey,
onGenerateRoute: Router().onGenerateRoute,
builder: (context, child) => NavFrame(_navKey, child),
);
}
}
nav_frame.dart
class NavFrame extends StatelessWidget {
final Widget child;
final GlobalKey<NavigatorState> _navKey;
const NavFrame(this._navKey, this.child, {Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
color: Colors.black,
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {
_navKey.currentState.pushReplacementNamed(Routes.view1);
},
child: Text('View 1'),
),
SizedBox(
width: 50,
),
RaisedButton(
onPressed: () {
_navKey.currentState.pushReplacementNamed(Routes.view2);
},
child: Text('View 2'),
),
],
),
)),
Positioned(
top: 100,
left: 0,
right: 0,
bottom: 0,
child: Container(
child: child,
),
),
],
);
}
}
view1.dart
class View1 extends StatelessWidget {
const View1({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Container(
color: Colors.blue,
))
],
);
}
}
view2.dart
class View2 extends StatefulWidget {
const View2({Key key}) : super(key: key);
#override
_View2State createState() => _View2State();
}
class _View2State extends State<View2>
with AutomaticKeepAliveClientMixin<View2> {
final bucket = PageStorageBucket();
#override
Widget build(BuildContext context) {
super.build(context);
return Column(
children: [
Expanded(
child: GridView.builder(
key: new PageStorageKey('test-key'),
addAutomaticKeepAlives: true,
padding: EdgeInsets.all(30),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 2,
crossAxisSpacing: 30,
mainAxisSpacing: 30,
),
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
border: Border.all(width: 1.5, color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Center(
child: Material(
child: Text(
index.toString(),
style: TextStyle(fontSize: 20),
),
),
));
},
),
)
],
);
}
#override
bool get wantKeepAlive => true;
}
router.dart
#MaterialAutoRouter(
generateNavigationHelperExtension: true,
routes: <AutoRoute>[
MaterialRoute(page: View1, initial: true),
MaterialRoute(page: View2, path: "/view2"),
],
)
class $Router {}
pubspec.yaml
name: PageStorageKeyTest
description: Debugging PageStorageKey.
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# Navigation
auto_route: 0.6.6
# Cupertino
cupertino_icons: ^0.1.3
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: 1.10.1
auto_route_generator:
flutter:
uses-material-design: true
As mentioned I've tried many different suggested solutions I've been able to find. Currently in View2 you'll see I'm using the AutomaticKeepAliveCluentMixin with wantKeepAlive set to true. I did also make sure to call the super.build(context) as I saw suggested in one question on here. I also have tried using the PageStoreBucket in just about every place I can imagine and it doesn't seem to be doing anything. (Currently not being used in the above code). I've tried to set maintainState to true in the MaterialRoute. In my actual project where I'm running into this I'm using Stacked architecture and even tried passing the keys through the viewModel and having the viewModel builder only build once and nothing seems to be doing the trick. Running the above code should produce the exact issue I'm having. I'm also on the latest dev build I believe (1.21.0-1.0.pre). Any suggestions?

I was looking at this the wrong way from a UX perspective. You can achieved the desired effect of what I was talking about using a scrollController and saving the offset to a global variable or something and just reassigning it in the initState method to the initialOffset parameter of the scrollController. I used stacked architecture personally and stored the offset in a service class with a reactiveServiceMixin. This works for what I described, but it's not what I actually wanted, which I didn't realize until doing it and seeing it for myself. If you use the navigator to navigate to for example a shop page and then go to a blog and then click shop again from the navigator you would probably expect to be back to the beginning of the shop. When clicking the back button from the blog page to go back to the shop you would then probably expect it to save the scroll position. Turns out it was already doing all of that before implementing this.

Related

Complete Dialog is not scrollable but just the listview inside it is scrollable

I am trying to create a modal which contains some details and is scrollable. The modal contains a ListView which is scrollable, but the complete modal is not.
I have tried adding the following options to the ListView, but it didn't help
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics()
The minimum reproducible code is
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
// Application name
title: 'Flutter Stateful Clicker Counter',
theme: ThemeData(
// Application theme data, you can set the colors for the application as
// you want
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Clicker Counter Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
#override
_MyHomePageState createState() => _MyHomePageState();
}
_buildTheDescriptionWizard() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
"Paneer is a fresh cheese used in a wide variety of Indian recipes, made by heating and then curdling milk using acid. It's very mild and milky in flavor, white in color, and its texture is soft, spongy, and squeaky. This texture helps it to absorb the flavors of sauces or marinades. It can be made from cow's milk or buffalo milk, either pasteurized or raw, and can be made from whole, skim or reduced-fat milk. ",
textAlign: TextAlign.left,
style: TextStyle(fontSize: 15, height: 1.5, color: Colors.black))),
);
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
elevation: 16,
child: createModal());
}
createModal() {
return Container(
height: 600,
width: 800,
child: Container(
color: Colors.white,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: <Widget>[
Container(
height: 300,
child: ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
Container(
width: 300,
color: Colors.white,
// child:
// DropShadow(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
'https://picsum.photos/250?image=9',
height: 250,
width: 250),
),
// )
),
Container(
width: 450, height: 300, color: Colors.black)
],
)),
Divider(),
_buildTheDescriptionWizard()
]))));
}
}
Can someone please help me figure out how I can make the complete modal scrollable ?
remove SingleChildScrollView and Column just use ListView and don't use shrinkWrap: true and physics: const NeverScrollableScrollPhysics()

Flutter: ListView - Green overlay instead of arrows in widget inspector

I'm creating a ListView with a builder function. I use the widget inspector to assess the any issues with the layout of the widgets.
Usually, the listView shows downwards green arrows as shown here:
[ListView layout][1]
However, in my current app, whenever I create a listView, it shows this green overlay on the listView. This creates artefacts with Image widget nested in stack; the images flicker when scrolling. [artefact layout][2]
This layout does look like it will take the space of 'drawer' in scaffold however, this page does not have a drawer, although all the other pages do.
Please find the code below for your reference.
const BlogListPage({Key? key}) : super(key: key);
#override
State<BlogListPage> createState() => _BlogListPageState();
}
class _BlogListPageState extends State<BlogListPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: ASAppBar(
title: const Text('Blogs'),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
margin:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
color: Colors.amber,
child: const SizedBox(
height: 100,
width: double.infinity,
),
);
},
));
}
} ```
[1]: https://i.stack.imgur.com/O7Pnc.png
[2]: https://i.stack.imgur.com/0g5HF.png

RenderFlex overflowed only on screen transition but no visual issues

First, let me start with I am new to Flutter and this is my first app.
I am running into an issue where I am getting the error:
════════ Exception caught by rendering library ═════════════════════════════════
A RenderFlex overflowed by 24 pixels on the bottom.
The relevant error-causing widget was
Column
lib/widgets/home_screen_cta.dart:42
════════════════════════════════════════════════════════════════════════════════
For the 4 buttons on my home screen.
The weird part is the screen looks fine. No yellow and black bars or anything. This only happens when I transition from the login screen to the home screen. If I move around the app, this does not happen. I do notice it on another form once it is submitted (only for the first submission, not for any others). It also looks bad for the first animation but after that it looks fine.
I was wondering if I should just ignore this but it seems like that is a bad idea.
I have tried changing this to use many other options. This includes:
Changed between Expanded and Flexible
Tried using a container/SizedBox instead
Tried to find another solution for the CTA Item but couldn't find anything that would do this look
At this point, I understand it's a overflow issue but there are no indications of an overflow appearing in the app.
I am trying to make the app responsive in nature so I believe I am doing okay for that. This RenderFlex overflow issue is really puzzling. The fact I don't see the yellow/black bars in the app makes me think this is happening during a transition and I wonder if I should be concerned about it?
Is there a way to control these animations? I really do not like them and I feel it's the transition causing the overflow issue... I'm a bit lost on this.
My code:
Home Screen (HomeDashboard):
import 'package:flutter/material.dart';
import '../app/components/app_drawer.dart';
import '../app/components/app_bar.dart';
import '../lang/app_localizations.dart';
import '../widgets/clock_text.dart';
import '../widgets/home_screen_cta.dart';
import '../widgets/home_screen_dashboard.dart';
import '../widgets/logo_horizontal.dart';
class HomeDashboard extends StatelessWidget {
const HomeDashboard({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: FreshFlowAppBar.get(title: AppLocalizations.of(context)!.title),
drawer: const AppDrawer(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Flexible(
flex: 1,
fit: FlexFit.loose,
child: LogoHorizontal(),
),
Flexible(
flex: 1,
fit: FlexFit.loose,
child: ClockText(),
),
Flexible(
flex: 6,
fit: FlexFit.tight,
child: HomeScreenDashboard(),
),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: HomeScreenCta(),
),
],
),
),
),
);
}
}
HomeScreenCta:
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:fresh_flow/services/user.service.dart';
import 'package:provider/provider.dart';
import '../lang/app_localizations.dart';
import '../screens/home/pre_op_entry.dart';
import '../screens/home/prod_entry.dart';
import '../screens/home/post_op_entry.dart';
import '../screens/home/downtime_entry.dart';
import '../services/pre-op.service.dart';
import '../utils/size_helpers.dart';
class HomeScreenCta extends StatefulWidget {
const HomeScreenCta({Key? key}) : super(key: key);
#override
State<HomeScreenCta> createState() => _HomeScreenCtaState();
}
class _HomeScreenCtaState extends State<HomeScreenCta> {
bool _isPreOpSetupCompleted = false;
/// Returns a Widget for the CTA item
Widget _getCtaItem(
BuildContext context,
String title,
IconData iconData,
String routeName,
bool isEnabled,
) {
return GestureDetector(
onTap: () {
if (isEnabled) {
Navigator.of(context).pushNamed(routeName);
}
},
child: Opacity(
opacity: isEnabled ? 1.0 : 0.4,
child: Column( // This is causing RenderFlex overflow issue
children: [
Icon(
iconData,
size: displayHeight(context) * .05,
),
const SizedBox(
height: displayHeight(context) * .03,
),
Text(
title,
style: TextStyle(
fontSize: displayWidth(context) * 0.03,
),
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
// Define services
final _preOpService = Provider.of<PreOpService>(
context,
listen: false, // No need to listen
);
final _userService = Provider.of<UserService>(
context,
listen: false, // No need to listen
);
_preOpService.init(_userService.getId()!);
// Only Pre-Op Entry is allowed until it is complete all others need to be disabled
if (_preOpService.hasCompletedFirstPreopOfTheDay()) {
setState(() {
_isPreOpSetupCompleted = true;
});
}
final _width =
(displayWidth(context) - MediaQuery.of(context).padding.left) *
0.24;
final _children = [
SizedBox(
width: _width,
child: _getCtaItem(
context,
l10n!.preOpEntry,
FontAwesome.wrench,
PreOpEntry.routeName,
true,
),
),
SizedBox(
width: _width,
child: _getCtaItem(
context,
l10n.prodEntry,
MaterialCommunityIcons.account_hard_hat,
ProdEntry.routeName,
_isPreOpSetupCompleted,
),
),
SizedBox(
width: _width,
child: _getCtaItem(
context,
l10n.postOpEntry,
Icons.task_alt,
PostOpEntry.routeName,
_isPreOpSetupCompleted,
),
),
SizedBox(
width: _width,
child: _getCtaItem(
context,
l10n.downtimeEntry,
Icons.flag_sharp,
DowntimeEntry.routeName,
_isPreOpSetupCompleted,
),
),
];
return Center(
child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
children: _children,
),
);
}
}

Using BackdropFilter affects other widgets in a Row Widget - Flutter Web

I am having an issue when trying to blur a container in a Row widget when the user hovers over it (on Chrome). I have a custom widget called PanelLink, which is supposed to shrink in size and blur the background when the cursor goes over it. It works, but for some reason when hovering over one it also blurs every widget to the left not just itself.
FrontPage.dart:
import 'package:flutter/material.dart';
import 'package:website_v2/CustomWidgets/PanelLink.dart';
class FrontPage extends StatefulWidget {
FrontPage();
#override
_FrontPageState createState() => _FrontPageState();
}
class _FrontPageState extends State<FrontPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Website'),
centerTitle: false,
backgroundColor: Colors.blue[900],
),
backgroundColor: Colors.blueGrey[900],
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PanelLink(false, 'assets/education.jpg'),
PanelLink(true, 'assets/about.jpeg'),
PanelLink(false, 'assets/projects2.jpg'),
],
),
),
);
}
}
PanelLink.dart:
import 'package:flutter/material.dart';
import 'dart:ui';
class PanelLink extends StatefulWidget {
PanelLink(this._blur, this.imageLoc);
final bool _blur;
final String imageLoc;
#override
PanelLinkState createState() => PanelLinkState(_blur, imageLoc);
}
class PanelLinkState extends State<PanelLink> {
PanelLinkState(this._blur, this.imageLoc);
bool isHovering = false;
bool _blur;
String imageLoc;
Widget build(BuildContext context) {
return Expanded(
child: InkWell(
onTap: () => null,
onHover: (hovering) {
setState(() => {isHovering = hovering});
},
child: Stack(children: [
AnimatedContainer(
duration: const Duration(milliseconds: 1000),
curve: Curves.ease,
margin: EdgeInsets.all(isHovering ? 20 : 0),
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: AssetImage(imageLoc), fit: BoxFit.cover)),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: isHovering ? 5 : 0, sigmaY: isHovering ? 5 : 0),
child: Container(
color: Colors.black.withOpacity(0.1),
)),
),
Text('Test')
]),
));
}
}
Here is a picture of the issue occurring. I am hovering the mouse over panel 2, but both panel 1 and 2 are blurred but not panel 3. If I hover over panel 1 only panel 1 is blurred. If I hover over panel 3, all of them are blurred.
I experimented by only making panel two have the BackdropFilter Widget, but panel 1 still got blurred when hovering over panel 2.
Since your using AnimatedContainer widget the problem might be it cant identify the difference between his child widget. Try adding a key value identifier
AnimatedContainer(
child: Container(
key: ValueKey("imageA"), //make sure that this is different on every widget, its better to passed some value here thats different on the other refactored widget
Try experimenting it might work for you
),
),

Flutter custom widget fails to display

I am a newbie to Flutter and Dart, and am trying to create a clickable container class that is based on the
GestureDetector class. My code for my container is shown below:
class CustomWidget extends GestureDetector
{
final aTitle;
CustomWidget(this.aTitle)
{
onTap() {
print("$aTitle was pressed!");
}
child: new Container(
padding: const EdgeInsets.all(8),
child: Text(aTitle),
color: Colors.blueGrey[200],
);
}
}
I am attempting to display this widget in my main application screen using the following code in the body of my app widget:
body: CustomScrollView(
primary: false,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(20),
sliver: SliverGrid.count(
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 2,
children: <Widget>[
CustomWidget('Test Display'),
],
),
),
],
),
I seem to have two problems: (1) my development environment is telling me that my onTap() method is "unused", indicating
that it will not capture tap events, and (2) that doesn't seem to matter much because the CustomWidget instance that I am
creating in the app widget is not appearing on my screen.
I am clearly missing something. Can someone help me correct my code so that my custom widget will be displayed and process
onTap events?
Generally speaking, the CustomWidget is a good idea, but inheritance is the wrong implementation. Flutter strongly favors composition over inheritance.
Your custom widget using composition:
class CustomWidget extends StatelessWidget {
final String title;
const CustomWidget(this.title);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print("$title was pressed!");
},
child: new Container(
padding: const EdgeInsets.all(8),
child: Text(title),
color: Colors.blueGrey[200],
));
}
}