How to use provider beetween several classes in flutter? - flutter

I am creating a register app in flutter, but there is too much information tu use a single screen, so I decided to use tab views in the screens and separate the screens in objects of a drawer.
but I needed to use the information from diferent screens to saved it. so I think about using a Multiprovider. but when I try to use the information in the provider it throws:
Could not find the correct Provider<General> above this Baseformularios Widget
my implementation is like this:
Here is my drawer with the first field General, that is basic information about the client,
as you can see the provider is created here and the body: is supplied by a variable called bodycontent, that will be over written when a try to change the form, but when I try to print the name of the user it throws the error above.
MultiProvider(
providers: [
ChangeNotifierProvider<General>(
create: (context)=>General(),
)
],
child: Scaffold(
appBar: AppBar(
title: Text("Historias clinicas"),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.check),
onPressed: (){
print(Provider.of<General>(context).nombre);
Provider.of<General>(context).addCLiente();
},
)
],
),
body: bodycontent,
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
AspectRatio(
aspectRatio: 0.001/0.001,
child: DrawerHeader(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.white,Colors.blueAccent]
),
),
),
ListTile(
title: Text("General"),
onTap: (){
setState(() {
bodycontent = I_II_III_IV();
});
Navigator.pop(context);
},
),
],
),
),
),
);
and here is the usage on the form general:
Provider.of<General>(context).set(nombre.text,
"Apellido hay que quitar en la funcion",
edad.text,
_currentsexo,
estado_civil.text,
direccion.text,
emergencia.text,
procedencia.text,
telefono.text,
ocupacion.text,
referencia.text,
fecha_inicio.text,
"",userid);
this save well the information, I checked it in the debugger, but the problem is in the other side.
I tried using the multiprovider in the screen before the one with the drawer, but it throwed the same error but with "above generalform". notice that I removed the multiprovider from the screen with the drawer when I tried this.
so should I declare the multiprovider in every screen to use it into it's son? because I supposed that provider where variables that you could use in every screen after the one where it's declare, or am I missunderstanding something?... Every help is very appreciated, thanks before hand.

To get access to the Provider from every where in your app, you should declare it in the main:
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider<General>(
create: (context) => General(),
),
],
child: MyApp(),
));
}

Related

Flutter Modal Bottom Sheet with Navigator does not pop as expected

I a working with ModalBottomSheet and I love the package. However I am encountering an issue when trying to navigate with in a ModalBottomSheet.
Here is a ScreenVideo for a better understanding.
As you can see the View is presented as a ModalBottomSheet. But when popping it, it is not simply dismissing to the bottom but instead it is popping to some empty screen with a MaterialPage Pop animation.
I changed my code so I can push with a MaterialPageRoute-Animation inside that ModalBottomSheet. Before that everything was working as expected.
Here is my Page:
class _AboutScreenState extends State<AboutScreen> {
#override
Widget build(BuildContext context) {
return Material(
color: AppColors.primary,
child: Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context2) => Builder(
builder: (context) => CupertinoPageScaffold(
backgroundColor: AppColors.secondary,
resizeToAvoidBottomInset: false,
child: SafeArea(
child: Padding(
padding: EdgeInsets.all(sidePadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButtonWithExpandedTouchTarget(
onTapped: () {
Navigator.pop(context);
},
svgPath: 'assets/icons/down.svg',
),
],
),
Text(
'About',
style: AppTextStyles.montserratH2SemiBold,
),
...
RoundedCornersTextButton(
title: 'Impressum',
onTap: () {
Navigator.pop(context);
},
textColor: AppColors.primary,
backgroundColor: AppColors.secondary,
borderColor: AppColors.primary,
),
],
),
),
)),
),
),
),
);
}
}
I was simply following this example. What am I missing here? With the code above I can push inside the ModalSheet as expected with a MaterialPageRoute Animation but popping the first screen brings up the issue...
Let me know if you need any more info! Any help is appreciated :)
I think you are popping with the wrong context. The example is popping with rootContext, which is from the top-most widget in the hierarchy. You are popping from with context, defined at your lowest builder in the hierarchy.
I believe you are using the incorrect context. rootContext, which is from the top-most widget in the hierarchy, is what makes the sample pop. You're popping from your lowest builder in the hierarchy, which is dictated by context.

Searching for the name of a specific menu - Flutter

So I'm searching for a Menu often found in a Settings Screen. It's used to select a Setting.
It opens when you press on a ListTile and then it fills the mayority of the Screen, while the borders are transparent. In the menu you have to select one of the options.
An example usecase of that would be to select a Language(onTap of the ListTile opens a Menu where you can select between all the avaliable languages)
It is similar to that of a PopupMenuButton, however as already mentioned it fills most of the screen, and the individual selectable items have a radiobutton in front of the text(probably RadioListTiles)
I hope someone gets what I'm talking about, because for the past 3 hours I'm searching for this widget but just find articles about the PopupMenuButton over and over.
Edit: I finally found an app that uses the feature I'm looking for
: https://imgur.com/a/A9k71io
By clicking on the first ListTile in the first Picture, the dialog in the second picture opens up.
Hopefully this custom widget is something roughly what you want, try to copy and paste it in your project and play with it.
this is made using the Dialog widget, to exit the Dialog you can tap out of it/ press the device back arrow and I added a small back icon on the top left corner.
tell me if it helped :)
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;// the device size
return Scaffold(
body: Center(
child: ListTile(
tileColor: Colors.grey[300],
title: Text(
'Language',
style: TextStyle(fontSize: 25),
),
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
insetPadding: EdgeInsets.all(20),
child: Container(
color: Colors.white,
height: deviceSize.height,
width: deviceSize.width,
child: Column(
children: [
Row(
children: [
IconButton(
color: Colors.red,
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
],
),
Spacer(),
Text('Pick A Language'),
Spacer(),
],
),
),
),
),
),
),
);
}
}

How retrieve two classes Flutter

i am green in flutter and i have quick question, would like to make retrieve two classes SearchBar() & HomeView(), but it only allows to retrieve one class. I understand that something is missing, i would like to hear your suggestions in order to learn it better.
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.add_box,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AddBookPage(uid: this.widget.uid)));
})
],
),
body: SearchBar(),
body: HomeView(),
drawer: NavigateDrawer(uid: this.widget.uid));
}
Here is the picture how i would like make it look like
I get Search Bar overflowed by Infinity pixels on the bottom
Use Column:
Scaffold(
body: Column(
children: [
SearchBar(),
SizedBox(height: 20),
Expanded(
child: HomeView(),
)
],
),
// ... your other code
)
Using a Column may be the best way.
The overflow error is probably caused by the SearchBar() widget not having a defined height. A Column widget can't have a child with infinite height.

Dialog state does not change after restarting (closing and opening again) in flutter

I have defined one button inside one StatelessWidget (This will have the bloc creation logic and injecting using bloc provider, ), on click of the button i am showing a dialog and passing the bloc instance to it, as shown in the code.
//EsignBloc is defined here in parent statelessWidget. Defined i.e. creating the bloc instance and passing through the BlocProvider. Removed the code for simplicity
//This listener will be called when Button defined inside statelessWidget will be clicked. this is responsible for showing the dialog.
void _onClickHere(
BuildContext context,
) {
final dialog = Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
),
elevation: 0.0,
backgroundColor: Colors.transparent,
child: _GetSignUsingOtpView(),
);
showDialog(
context: context,
builder: (_) => BlocProvider<EsignBloc>(
create: (_) => BlocProvider.of<EsignBloc>(context), // passing already created bloc to dialog
child: WillPopScope(
onWillPop: () => Future.value(false),
child: dialog,
),
),
barrierDismissible: false,
);
}
Pasting some code of _GetSignUsingOtpView()
class _GetSignUsingOtpView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<EsignBloc, EsignState>(builder: (context, state) {
return Container(
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(
AppConstants.borderRadius,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () => _closeDialog(context),
child: Padding(
padding: const EdgeInsets.only(top: 8.0, right: 8),
child: Icon(
Icons.cancel,
color: AppColor.primaryDark,
size: SizeConfig.safeBlockVertical * 2,
),
),
),
),
Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
PrimaryText(text: state.otp), // data does not change after closing and opeing dialog again
PrimaryText(text: state.remainingTime), // data does not change after closing and opeing dialog again
],
),
),
],
),
);
});
}
void _closeDialog(BuildContext context) {
Navigator.pop(context);
}
}
The problem that I am facing is whenever the dialog opens again after closing, it doesn't show the latest data from the bloc. The dialog just shows whatever previous data is in the bloc. Can someone point it out, where i am making the mistake?
The reason that your dialog is not showing new data is because you are creating a new instance of the bloc. Even though you say that you're not.
BlocProvider<EsignBloc>(
create: (_) => BlocProvider.of<EsignBloc>(context), // passing already created bloc to dialog
child: ...
In some cases, BlocProvider can be used to provide an existing cubit to a new portion of the widget tree. This will be most commonly used when an existing cubit needs to be made available to a new route. In this case, BlocProvider will not automatically close the cubit since it did not create it.
To not create a new instance, you need to use BlocProvider.value. You can read more about it here
BlocProvider.value(
value: BlocProvider.of<EsignBloc>(context),
child: ...,
);
Instead of creating a new instance, this will use the previous instance and provide it to the child widget and will NOT close the bloc when its done using it. This is handy for dialogs and navigation.

Flutter bottomNavigationBar (change only body section)

Ok so I have got the following bottomNavigationBar working, however it's not clean enough for me and I am hoping there is a better way that someone knows.
Basically I only want to change the body section and not the full page.
However I want to have each section in different classes to keep it neat (ideally new .dart files - as they will all have different functions)
Currently all the page info is inside the body tag
body: PageView(
controller: _myPage,
onPageChanged: (int) {
print('Page Changes to index $int');
},
children: <Widget>[
Center(
child: Container(
child: Text('Empty Body 0'),
),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('images/pic1.jpg'),
Text('images/pic2.jpg'),
Text('images/pic3.jpg'),
],
),
),
Center(
child: Container(
child: Text('Empty Body 2'),
),
),
Center(
child: Container(
child: Text('DRN1 app is made using Google Flutter. While every attempt was made to make sure that this app works on as many devices as possible. We are unable to test every device. If you have issues running this app please let us know'),
),
)
],
physics: NeverScrollableScrollPhysics(), // Comment this if you need to use Swipe.
),
what I would like is something like this.
body: PageView(
controller: _myPage,
onPageChanged: (int) {
print('Page Changes to index $int');
},
children: <Widget>[
home(),
news() , // this is the main body for home
shows(), // this one shows the class shows() which fetches JSON and returns a different body layout
about(), // about text
]
Does anyone know of a better way to do this?
What you can do is create separate Dart files and import them into your main.dart
Then inside your State class define a list which contains the pages that you want to show
List<Widget> _myPage = <Widget>[Page1class(),Page2class(),Page3class()];
After that you can use the below code which looks quite clean. Using the builder() method will allow you to create pages based on the size of _myPage,
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: Text('sdf')),
body: PageView.builder(
itemBuilder: (context, position) => _myPage[position],
itemCount: _myPage.length,
),
)
);
Here is a sample program: PageView sample