flutter provider what is the difference with 'Context context' and 'context ' - flutter

```
import 'package:a_class_flutter/provider/ac_score/ac_score_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
class ACScoreView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ACScoreProvider(),
builder: (context, child) {
return Stack(
children: <Widget>[
_topMenu(context),
],
);
},
);
}
}
// Important!
Widget _topMenu(BuildContext context) {
return GestureDetector(
onTap: () {
context.read<ACScoreProvider>().showTopMenueStatus();
},
child: Container(
width: double.infinity,
height: double.infinity,
color: Colors.red,
child: Stack(
children: <Widget>[
Positioned(
child: AnimatedOpacity(
duration: Duration(microseconds: 100),
opacity:
context.watch<ACScoreProvider>().needShowTopMenue ? 1.0 : 0.0,
child: Container(
width: double.infinity,
height: ScreenUtil().setHeight(60),
color: Colors.black,
),
),
)
],
),
),
);
}
```
This is my code which is working well, but when I try to change Widget _topMenu(BuildContext context) to Widget _topMenu(context), Flutter gives me an error which is:
The following NoSuchMethodError was thrown building Builder(dirty, dependencies: [_InheritedProviderScope<ACScoreProvider>]): Class 'StatelessElement' has no instance method 'watch'. Receiver: Instance of 'StatelessElement'. Tried calling: watch<ACScoreProvider>().The relevant error-causing widget was ChangeNotifierProvider<ACScoreProvider> package:a_class_flutter/…/ac_score/ac_score.dart:9.
Why missing BuildContext declaration could cause this error? Do I need to put every context with BuildContext?

From the provider docs:
The easiest way to read a value is by using the extension methods on [BuildContext]:
context.read<T>(), which returns T without listening to it
Writing just Widget _topMenu(context), the context parameter is inferred as a dynamic context not BuildContext context and hence you it does not have the extension method .read<T>() you are trying to call.
If it were inside a StatefulWidget, this wouldn't have been a problem because it's State class has a instance context (type BuildContext) property and so the extension method .read<T>() can be called on it.

Related

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,
),
);
}
}

Flutter : using changeNotifier and provider when the context is not available

I'm trying to use the simple state management described in the Flutter docs, using a ChangeNotifier, a Consumer, and a ChangeNotifierProvider.
My problem is that I can't get a hold a on valid context to update my model (details below...). I get an error:
Error: Error: Could not find the correct Provider above this CreateOrganizationDialog Widget
This likely happens because you used a BuildContext that does not include the provider of your choice. There are a few common scenarios:
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that CreateOrganizationDialog is under your MultiProvider/Provider.
This usually happen when you are creating a provider and trying to read it immediately.
Here are extracts of my code:
class OrganizationModel extends ChangeNotifier {
final List<Organization> _items = [];
/// An unmodifiable view of the items in the cart.
UnmodifiableListView<Organization> get items => UnmodifiableListView(_items);
void addList(List<Organization> items) {
_items.addAll(items);
notifyListeners();
}
}
This is my model.
class OrganizationBodyLayout extends StatelessWidget {
Future<void> _showCreateOrganizationDialog() async {
return showDialog<void>(
context: navigatorKey.currentState.overlay.context,
barrierDismissible: false,
child: CreateOrganizationDialog());
}
_onCreateOrganizationPressed() {
_showCreateOrganizationDialog();
}
_onDeleteOrganizationPressed() {
//TODO something
}
_onEditOrganizationPressed() {
//TODO something
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(mainAxisSize: MainAxisSize.max, children: [
ButtonBar(
alignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
RaisedButton(
onPressed: _onCreateOrganizationPressed,
child: Text("New Organization"),
),
],
),
Expanded(
child: Container(
color: Colors.pink,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: ChangeNotifierProvider(
create: (context) => OrganizationModel(),
child: OrganizationListView(),
)),
Expanded(child: Container(color: Colors.brown))
]))),
]));
}
}
A stateless widget that contains a ChangeNotifierProvider just on top of the list widget using the model.
On a button click, a modal dialog is shown, then data is fetched from the network. I should then update my model calling the addList operation.
Below is the code for the stateful dialog box.
class CreateOrganizationDialog extends StatefulWidget {
#override
_CreateOrganizationDialogState createState() =>
_CreateOrganizationDialogState();
}
class _CreateOrganizationDialogState extends State<CreateOrganizationDialog> {
TextEditingController _nametextController;
TextEditingController _descriptionTextController;
#override
initState() {
_nametextController = new TextEditingController();
_descriptionTextController = new TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Dialog(
child: Container(
width: 200,
height: 220,
child: Column(
children: [
Text('New organization',
style: Theme.of(context).textTheme.headline6),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
decoration: new InputDecoration(hintText: "Organization name"),
controller: _nametextController,
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
decoration:
new InputDecoration(hintText: "Organization description"),
controller: _descriptionTextController,
),
),
ButtonBar(
children: [
FlatButton(
child: new Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: new Text("Create"),
onPressed: () {
setState(() {
Future<Organization> organization =
backendCreateOrganization(_nametextController.text,
_descriptionTextController.text);
organization.then((value) {
Future<List<Organization>> organizations =
backendReloadOrganizations();
organizations.then((value) {
var model = context.read<OrganizationModel>();
// var model = navigatorKey.currentState.overlay.context.read<OrganizationModel>();
//model.addList(value);
});
});
});
Navigator.of(context).pop();
//context is the one for the create dialog here
},
)
],
)
],
),
));
}
}
My problem happens at the line
var model = context.read<OrganizationModel>();
Thinking of it, the context available here is the modal dialog box context - so it's kind of logical that the Provider is not found in the widget tree.
However, I can't see how to retrieve the proper context (which would be the one for the result list view, where the Provider is located) in order to get the model and then update it.
Any idea is welcome :-)
Solved (kind of).
The only way I've found to solve this is by making my model a global variable:
var globalModel = OrganizationModel();
And referencing this global model in all widgets that consume it. I can't find a way to find the context of a stateless widget from within a callback in another stateful widget.
It works, but it's ugly. Still open to elegant solutions here :-)
Get_it seems to be elegant way of sharing models across the application. Please check the documentation for the different use cases they provide.
You could do something like the following
GetIt getIt = GetIt.instance;
getIt.registerSingleton<AppModel>(AppModelImplementation());
getIt.registerLazySingleton<RESTAPI>(() =>RestAPIImplementation());
And in other parts of your code, you could do something like
var myAppModel = getIt.get<AppModel>();

Resizing parent widget to fit child post 'Transform' in Flutter

I'm using Transforms in Flutter to create a scrolling carousel for selecting from various options.
This uses standard elements such as ListView.builder, which all works fine, aside from the fact that the parent widget of the Transform doesn't scale down to fit the content as seen here:
Here's the code used to generate the 'card' (there was actually a Card in there, but I've stripped it out in an attempt to get everything to scale correctly):
return Align(
child: Transform(
alignment: Alignment.center,
transform: mat,
child: Container(
height: 220,
color: color,
width: MediaQuery.of(context).size.width * 0.7,
child: Text(
offset.toString(),
style: TextStyle(color: Colors.white, fontSize: 12.0),
),
),
),
);
}
Even if I remove the 'height' parameter of the Container (so everything scales to fit the 'Text' widget), the boxes containing the Transform widgets still have the gaps around them.
Flutter doesn't seem to have any documentation to show how to re-scale the parent if the object within is transformed - anyone here knows or has any idea of a workaround?
EDIT: The widget returned from this is used within a build widget in a Stateful widget. The stack is Column > Container > ListView.builder.
If I remove the Transform, the Containers fit together as I'd like - it seems that performing a perspective transform on the Container 'shrinks' it's content (in this case, the color - check the linked screen grab), but doesn't re-scale the Container itself, which is what I'm trying to achieve.
I have a tricky solution for this: addPostFrameCallback + overlay.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
// ignore: must_be_immutable
class ChildSizeWidget extends HookWidget {
final Widget Function(BuildContext context, Widget child, Size size) builder;
final Widget child;
final GlobalKey _key = GlobalKey();
OverlayEntry _overlay;
ChildSizeWidget({ this.child, this.builder });
#override
Widget build(BuildContext context) {
final size = useState<Size>(null);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
_overlay = OverlayEntry(
builder: (context) => Opacity(
child: SingleChildScrollView(
child: Container(
child: child,
key: _key,
),
),
opacity: 0.0,
),
);
Overlay.of(context).insert(_overlay);
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
size.value = _key.currentContext.size;
_overlay.remove();
});
});
return () => null;
}, [child]);
if (size == null || size.value == null) {
return child;
} else {
return builder(context, child, size.value);
}
}
}
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class HomeView extends HookWidget {
#override
Widget build(BuildContext context) {
final change = useState<bool>(false);
final normal = Container(
color: Colors.blueAccent,
height: 200.0,
width: 200.0,
);
final big = Container(
color: Colors.redAccent,
height: 300.0,
width: 200.0,
);
return Column(
children: [
Container(
alignment: Alignment.center,
child: ChildSizeWidget(
child: change.value ? big : normal,
builder: (context, child, size) => AnimatedContainer(
alignment: Alignment.center,
child: SingleChildScrollView(child: child),
duration: Duration(milliseconds: 250),
height: size.height,
),
),
color: Colors.grey,
),
FlatButton(
child: Text('Toggle child'),
onPressed: () => change.value = !change.value,
color: Colors.green,
),
],
);
}
}
I have a menu with several options, they have different height and with the help of the animations this is ok, it's working really nice for me.
Why are you using Align, as much as I can see in your code, there is no property set or used, to align anything. So try removing Align widget around Transform.
Because according to the documentation, Transform is such a widget that tries to be the same size as their children. So that would satisfy your requirement.
For more info check out this documentation: https://flutter.dev/docs/development/ui/layout/box-constraints
I hope it helps!

Could not find the correct Provider

I'm new to using flutter and i was trying to implement a covid 19 tracker of cases in the world and i got this error when creating a custom widget to display number of cases , can anyone help me figure out the solution to this exactly as i have been trying to fix it for hours now.
This is the exception caught by the widgets library:
Error: Could not find the correct Provider above this NewCasesCard Widget
To fix, please:
Ensure the Provider is an ancestor to this NewCasesCard Widget
Provide types to Provider
Provide types to Consumer
Provide types to Provider.of()
Ensure the correct context is being used.
If none of these solutions work, please file a bug at:
https://github.com/rrousselGit/provider/issues
Here is the code:
import 'package:stay_safe/Providers/AppBrain.dart';
import 'package:stay_safe/utils/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
class NewCasesCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return getCard(context);
}
Widget getCard(context){
if (Provider.of<AppBrain>(context).isloading2)
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: SpinKitPumpingHeart(color: AppTheme().kcolors[0],),
),
);
else {
final countryInfo = Provider.of<AppBrain>(context).countryStats['countrydata'][0];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 0.5,
color: Colors.white70,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('New Cases Today',style: GoogleFonts.cabin(fontSize: 25),),
),
SizedBox(height: 8,),
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
StatIcon(countryInfo['total_active_cases'], 'Active', AppTheme().kcolors[0]),
StatIcon(countryInfo['total_new_cases_today'], 'New Cases', AppTheme().kcolors[2]),
StatIcon(countryInfo['total_new_deaths_today'], 'Deaths', AppTheme().kcolors[1])
],),
)
],
),
),
);
}
}
}
Widget StatIcon(int count,String type,Color color){
return Column(
children: <Widget>[
Text(count.toString(),style: GoogleFonts.cabin(color: color,fontWeight: FontWeight.bold,fontSize: 20),),
Text(type,style: GoogleFonts.cabin(color: color,fontWeight: FontWeight.bold,fontSize: 20),),
],
);
}
The issue looks like you have created the AppBrain provider in a different part of your widget tree to NewCasesCard, so when NewCasesCard attempts to find the provider by searching the tree it cannot. You need to make sure that the AppBrain provider is above NewCasesCard somewhere in the widget tree. So for example in the screen that includes this widget you could have:
class NewCasesScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Provider<AppBrain>(
create: (_) => AppBrain(),
child: Scaffold(
body: ListView(
children: [
NewCasesCard(),
...
]
),
),
);
}
Alternatively you could initialise the provider at the top of your app since, from the name, it looks like it is used throughout. For this you could utilise the MultiProvider and have:
(Example taken from the package readme)
void main {
runApp(MultiProvider(
providers: [
Provider<AppBrain>(create: (_) => AppBrain()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
],
child: MaterialApp(...),
));
}
This way you can be sure that the provider will always be found.

Navigator and Provider conflict in flutter

sending data between screen using Provider with Navigator concept make a conflict
error after the run
The following ProviderNotFoundError was thrown building SecondRoute(dirty):
Error: Could not find the correct Provider above this SecondRoute Widget
To fix, please:
Ensure the Provider is an ancestor to this SecondRoute Widget
Provide types to Provider
Provide types to Consumer
Provide types to Provider.of()
Always use package imports. Ex: `import 'package:my_app/my_code.dart';
Ensure the correct context is being used.
https://www.ideone.com/xHXK5m
you can pass data like this put data in class
RaisedButton(
child: Text(‘Send data to the second page’),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(
data: data,
)),
);
},
),
and recieve data like this
class SecondPage extends StatelessWidget {
final Data data;
SecondPage({this.data});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Constructor — second page’),
),
body: Container(
padding: EdgeInsets.all(12.0),
alignment: Alignment.center,
child: Column(
children: <Widget>[
Container(
height: 54.0,
padding: EdgeInsets.all(12.0),
child: Text(‘Data passed to this page:’,
style: TextStyle(fontWeight: FontWeight.w700))),
Text(‘Text: ${data.text}’),
Text(‘Counter: ${data.counter}’),
Text(‘Date: ${data.dateTime}’),
],
),
),
);
}
You can try placing whatever Provider value you have above the MaterialApp and thus the navigation or, when you push to a second page, provide the value again. Providers are scoped to widget trees, so this is expected behavior.
As an example, to pass a value to another route, you could do something like
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
final foo = 'foo';
Provider<String>.value(
value: foo,
child: NewPage(),
);
}));
},
And then just consume it like regular in the NewPage route.
Try this code:
MaterialApp(
title: 'Navigation Basics',
home: ChangeNotifierProvider(
builder: (context) => Data(),
child: FirstRoute(),
))
Delete ChangeNotifierProvider in FirstRoute