How use ChangeNotifier and Consumer in custom flutter app bar? - flutter

I'm using MultiProvider in my home page on flutter app and work done.
But I have a custom App bar which contain a ChangeNotifierProvider with a consumer and not working. AppBar widget are not notify when notifyListener() function called.
home view:
/// Home screen.
class HomeScreen extends StatefulWidget {
/// Constructor
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _homeViewModel = HomeViewModel();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(),
body: MultiProvider(
providers: [
ChangeNotifierProvider.value(value: _homeViewModel),
ChangeNotifierProxyProvider<HomeViewModel, SecondViewModel>(
create: (context) => SecondViewModel(),
update: (context, homeViewModel, secondViewModel) =>
secondViewModel!..load()),
],
child: Container()
}
}
app bar view:
/// Custom App Bar.
class CustomDropAppBar extends cs.AppBar {
/// Title.
final Widget? appBarTitle;
/// Constructor.
const CustomDropAppBar(
{Key? key,
this.appBarTitle,})
: super(key: key);
#override
State<CustomMenuAppBar> createState() => _CustomMenuAppBarState();
}
/// App bar state.
class _CustomMenuAppBarState extends State<CustomMenuAppBar> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CustomMenuViewModel(),
child: Consumer<CustomMenuViewModel>(
builder: (context, viewModel, child) => cs.AppBar(
title: widget.appBarTitle,
actions: [Container()])));
}
I think it's because I'm using MultiProvider in the body Scalford function? or it's because AppBar cannot containing consumer?
How I can fix this problem?

Related

How to use RestorableChangeNotifier (flutter)

I'm using Provider and a ChangeNotifier to access some data across multiple screens in my app. I'm now wanting to implement restoration, so that even if that app dies in the background the user will come back to the same page with the same data. I have managed to make the app go back to the same page, but I can't figure out how to implement a restorable version of my ChangeNotifier. I found a RestorableChangeNotifier class on the Flutter API but am struggling to use it. I've put a simple code below that demonstrates the issue that I'm trying to achieve - any help would be appreciated!
Super simplified example
Class that uses ChangeNotifier, that stores the data:
class ItemClass extends ChangeNotifier {
final List<String> _itemNames = [];
List<String> get itemNames => _itemNames;
void addItem(String newItem) {
_itemNames.add(newItem);
notifyListeners();
}
}
Top level widget with ChangeNotifierProvider:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ItemClass(),
child: const MaterialApp(
home: HomeScreen(),
restorationScopeId: 'root',
),
);
}
}
The screen I want to be at, showing the info from the ItemClass:
class Screen1 extends StatefulWidget {
const Screen1({Key? key}) : super(key: key);
#override
State<Screen1> createState() => _Screen1State();
}
class _Screen1State extends State<Screen1> {
final TextEditingController _textEditingController = TextEditingController();
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ItemClass itemClass = context.watch<ItemClass>();
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
Text('Items: ${itemClass.itemNames}'),
TextField(
controller: _textEditingController,
),
TextButton(
child: const Text('Add item'),
onPressed: () =>
itemClass.addItem(_textEditingController.text)),
],
)));
}
}
I don't know where or how to implement the restoration for this ItemClass.

how can i solve Flutter Provider debug Error

I have some error in the Flutter code main.dart file .I create AuthService class,I create Provider method in the main.dart file, but it has some error at the call up the Provider method . i need solve this error.
enter image description here
import 'package:flutter/material.dart';
import 'package:trip/screen/first_screen.dart';
import 'package:trip/screen/sign_up_screen.dart';
import 'package:trip/services/auth_service.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Provider(
auth: AuthService,
child: MaterialApp(
title: 'Test FireBase',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: FirstScreen(),
routes: <String, WidgetBuilder>{
'/signUp': (BuildContext context) => SignUpScreen(),
'/home': (BuildContext context) => FirstScreen(),
},
),
);
}
}
class Provider extends InheritedWidget {
final AuthService auth;
Provider(Key key, Widget child, this.auth) : super(key: key, child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static Provider of(BuildContext context) =>
(context.dependOnInheritedWidgetOfExactType<Provider>() as Provider);
}
You are getting the error because the Provider constructor you made is working with positional parameters, not with named. For more information on named and positional parameters check out this post.
You can either change your constructor to work with named parameters like this:
Provider({Key? key, required Widget child, required this.auth}) : super(key: key, child: child);
or you can remove namings of your parameters in the cosntructor call like this:
return Provider(
UniqueKey(),
MaterialApp(
...
),
AuthService(),
);
I would prefer the first solution.

No constructor 'MaterialApp.' with matching arguments declared in class 'MaterialApp'

I just created a new flutter project and started organized my folders, this is what I have for now:
I'm trying to run it on Windows since I'll use Flutter Desktop for this project, and when I hit run it builds and runs just fine, but when I do a hot-reload the application throws this error message:
No constructor 'MaterialApp.' with matching arguments declared in class 'MaterialApp'.
Receiver: MaterialApp
Tried calling: new MaterialApp.()
Found: new MaterialApp.({Key? key, GlobalKey<NavigatorState>? navigatorKey, GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey, Widget? home, Map<String, (BuildContext) => Widget> routes, String? initialRoute, ((RouteSettings) => Route<dynamic>?)? onGenerateRoute, ((String) => List<Route<dynamic>>)? onGenerateInitialRoutes, ((RouteSettings) => Route<dynamic>?)? onUnknownRoute, List<NavigatorObserver> navigatorObservers, ((BuildContext, Widget?) => Widget)? builder, String title, ((BuildContext) => String)? onGenerateTitle, Color? color, ThemeData? theme, ThemeData? darkTheme, ThemeData? highContrastTheme, ThemeData? highContrastDarkTheme, ThemeMode? themeMode, Locale? locale, Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates, ((List<Locale>?, Iterable<Locale>) => Locale?)? localeListResolutionCallback, ((Locale?, Iterable<Locale>) => Locale?)? localeResolutionCallback, Iterable<Locale> supportedLocales, bool debugShowMaterialGrid, bool showPerformanceOverlay, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers, bool showSemanticsDebugger, bool debugShowCheckedModeBanner, Map<LogicalKeySet, Intent>? shortcuts, Map<Type, Action<Intent>>? actions, String? restorationScopeId}) => MaterialApp
This is the content of my main.dart:
import 'package:flutter/material.dart';
import 'package:beryllium/src/screens/HomeScreen.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Beryllium',
initialRoute: '/',
routes: {
'/': (contex) => HomeScreen(), //Home screen widget
},
);
}
}
And this the content of the HomeScreen.dart file:
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return new _HomeScreen();
}
}
class _HomeScreen extends StatelessWidget {
const _HomeScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Book'),
),
body: Center(
child: Container(
child: Text('Home Screen'),
),
));
}
}
If someone has any idea about avoiding this error, please give me a hand.
Thanks in advance.

How to change the page using Navigator.push so that the body and header animation is different, while the footer remains unchanged

I am trying to go to the next page using Navigator.push and at the same time change only the body on the page. I only got this when, for example, I wrap the index of page 2 in materialApp. But when I decided to make the animation (it smoothly pushes the old page to the left and pushes the new page to the right), it turned out that she pushed the old page, but behind it was exactly the same motionless page, which was later blocked by the new one.
I understood this in such a way that the first deleted page was an index 2 page, which is wrapped in MaterialApp, and behind it is exactly the same fixed MaterialApp for the entire application. At the moment, I have no idea how to remove a fixed page. I gave a picture of how I am currently navigating in the application, it may not be perfect, but I do not know better, any help would be appreciated.
In many applications, I see such an animation that the header fades out smoothly and at the same time a new one appears. And the body at this moment is replaced with the old page with a smooth movement, I really like it and I want to do the same.
You can try to use nested Navigator inside your scaffold.
Page index 1,2 and 3 will be inside the root Navigator under material app. Page 2 will contain another Navigator to fit your purpose.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final GlobalKey<NavigatorState> outgoingKey = GlobalKey<NavigatorState>();
return MaterialApp(
title: 'Sample',
home: Scaffold(
body: PageView(
children: <Widget>[
Page1(),
Page2(navigatorKey: outgoingKey,),
Page3(),
],
pageSnapping: false,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
),
bottomNavigationBar: /*SomeBottomNavigationBar()*/,
),
);
}
}
class Page2 extends StatelessWidget {
Page2({Key key, this.navigatorKey}) : super(key: key);
final GlobalKey<NavigatorState> navigatorKey;
#override
Widget build(BuildContext context) {
return Container(
child: Column(children: [
Expanded(
child: Navigator(
key: navigatorKey, // you need to use this to pop i.e. navigatorKey.currentState.pop()
initialRoute: 'initialPageIndex2',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder _builder;
switch (settings.name) {
case 'nextPageForPageIndex2':
_builder = (context) => /*NextPageForPageIndex2()*/;
break;
case 'initialPageIndex2':
default:
_builder = (context) => /*InitialPageIndex2()*/;
}
return MaterialPageRoute(builder: _builder);
},
transitionDelegate: DefaultTransitionDelegate(),
);
)
],)
);
}
}
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Page1'),
);
}
}
class Page3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Page3'),
);
}
}
You have to use the PageView please check the code below, hope it will help you :
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Basic AppBar'),
),
body: PageView(
children: <Widget>[
Page1(),
Page2()
],
pageSnapping: false,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
),
);
}
}
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
return Container(
child: Center(child:Text("Page 1")),
color: Colors.red,
);
}
}
class Page2 extends StatefulWidget {
#override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
#override
Widget build(BuildContext context) {
return Container(
child: Center(child:Text("Page 2")),
color: Colors.blueAccent,
);
}
}

flutter: how are dependencies on inherited widgets discovered?

I'm currently reading the example code of the provider package:
// ignore_for_file: public_member_api_docs
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: Consumer<Counter>(
builder: (context, counter, _) {
return MaterialApp(
supportedLocales: const [Locale('en')],
localizationsDelegates: [
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
_ExampleLocalizationsDelegate(counter.count),
],
home: const MyHomePage(),
);
},
),
);
}
}
class ExampleLocalizations {
static ExampleLocalizations of(BuildContext context) =>
Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);
const ExampleLocalizations(this._count);
final int _count;
String get title => 'Tapped $_count times';
}
class _ExampleLocalizationsDelegate
extends LocalizationsDelegate<ExampleLocalizations> {
const _ExampleLocalizationsDelegate(this.count);
final int count;
#override
bool isSupported(Locale locale) => locale.languageCode == 'en';
#override
Future<ExampleLocalizations> load(Locale locale) =>
SynchronousFuture(ExampleLocalizations(count));
#override
bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Title()),
body: const Center(child: CounterLabel()),
floatingActionButton: const IncrementCounterButton(),
);
}
}
class IncrementCounterButton extends StatelessWidget {
const IncrementCounterButton({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context).increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
const CounterLabel({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final counter = Provider.of<Counter>(context);
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
);
}
}
class Title extends StatelessWidget {
const Title({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(ExampleLocalizations.of(context).title);
}
}
When the user presses the FloatingRadioButton within IncrementCounterButton, build() is called on CounterLabel and IncrementCounterButton.
They both depend on an inherited widget, which is updated.
How does flutter discover this dependency?
I assume that the BuildContext is modified by the call to Provider.of<>().
Is this why we add the IncrementCounterButton, which has no functionality on its own?
Just to move the call to Provider.of<>() outside of its bigger parent widget, which would be more expensive to rebuild?
The binding widget an InheritedWidget and its consumers is created through BuildContext.
Consider the following InheritedWidget:
class Foo extends InheritedWidget {}
Then the descendants of Foo can subscribe to it by calling:
BuildContext context
context.inheritFromWidgetOfExactType(Foo);
It's worth noting that a widget can obtain the InheritedWidget without subscribing to it, by instead doing:
BuildContext context
context.ancestorInheritedElementForWidgetOfExactType(Foo);
This call is usually performed internally by the .of(context) pattern.
In the case of provider, that subscription is done by calling Provider.of<T>(context).
provider also exposes an optional argument to purposefully not subscribe to the inherited widget:
T value = Provider.of<T>(context, listen: false);