I try to use Polymorphism with the Provider package in Dart/Flutter, but I'm not sure if it is possible or not and if I have made a mistake.
I have two provider class "Provider1" and "Provider2" which extend an abstract class "AbstactProvider", I have a widget "WidgetProvider1Or2"
which need to interact with Provider1 Or Provider2 but flutter throw me a "ProviderNotFoundException".
thanks for yours help!
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_concept/cubit/counter_cubit.dart';
import 'package:provider/provider.dart';
abstract class AbstactProvider extends ChangeNotifier {
void printName();
}
class Provider1 extends AbstactProvider {
#override
void printName() {
print("Provider1");
}
}
class Provider2 extends AbstactProvider {
#override
void printName() {
print("Provider2");
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterCubit(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
Widget widgetProvider1 = ChangeNotifierProvider(
create: (_) => Provider1(),
child: WidgetProvider1(),
);
Widget widgetProvider2 = ChangeNotifierProvider(
create: (_) => Provider2(),
child: WidgetProvider2(),
);
Widget widgetProvider1Or2 = ChangeNotifierProvider(
create: (_) => Provider1(),
child: WidgetProvider1Or2(),
);
return Scaffold(
body: Column(
children: [widgetProvider1, widgetProvider2, widgetProvider1Or2],
),
);
}
}
class WidgetProvider1 extends StatelessWidget {
const WidgetProvider1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
context.watch<Provider1>();
return Container();
}
}
class WidgetProvider2 extends StatelessWidget {
const WidgetProvider2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
context.watch<Provider2>();
return Container();
}
}
class WidgetProvider1Or2 extends StatelessWidget {
const WidgetProvider1Or2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
context.watch<AbstactProvider>();
return Container();
}
}
Related
The code was working well till when I tried Extract CupertinoNavigationBar to a widget.
May Anyone please give some information why this issue happening?
It gives me this error:
Error: The argument type 'HomePageAppBar' can't be assigned to the parameter type 'ObstructingPreferredSizeWidget?'.
package:flutter_todo_app/main.dart:27
'HomePageAppBar' is from 'package:flutter_todo_app/main.dart' ('lib/main.dart').
package:flutter_todo_app/main.dart:1
'ObstructingPreferredSizeWidget' is from 'package:flutter/src/cupertino/page_scaffold.dart' ('../../development/flutter/packages/flutter/lib/src/cupertino/page_scaffold.dart').
package:flutter/…/cupertino/page_scaffold.dart:1
navigationBar: HomePageAppBar(),
^
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'widgets/body.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return CupertinoApp(
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: HomePageAppBar(),
child: HomePageBody(),
);
}
}
class HomePageAppBar extends StatelessWidget {
const HomePageAppBar({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return CupertinoNavigationBar(
middle: const Text('app bar'),
);
}
}
wrap your HomePageAppBar() with CupertinoNavigationBar() like this :
CupertinoNavigationBar(
child: HomePageAppBar(),
)
Observe that CupertinoPageScaffold navigationBar accepts ObstructingPreferredSizeWidget but you are providing a Stateless Widget that's what the error says. Try implementing HomePageAppBar with ObstructingPreferredSizeWidget (refer to the below code). I ran your code on my machine it solved the error.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'widgets/body.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return CupertinoApp(
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: HomePageAppBar(),
child: HomePageBody(),
);
}
}
// <<<<<<<------ CHANGES ------>>>>>>
class HomePageAppBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
const HomePageAppBar({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return const CupertinoNavigationBar(
middle: Text('app bar'),
);
}
#override
Size get preferredSize => const Size(double.infinity, 55);
#override
bool shouldFullyObstruct(BuildContext context) {
return false;
}
}
This question already has answers here:
Flutter calling child class function from parent class
(6 answers)
Closed 7 months ago.
How do I call method of child widget?
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
#override
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return Column(
children: [
FloatingActionButton(onPressed: (){
//call child function named funcToCallFromParent
}),
Child(),
],
);
}
}
class Child extends StatefulWidget {
const Child({Key? key}) : super(key: key);
#override
State<Child> createState() => _ChildState();
}
class _ChildState extends State<Child> {
void funcToCallFromParent(){
print('child func called from parent');
}
#override
Widget build(BuildContext context) {
return Container();
}
}
You can use ChangeNotifier to notify the child (or children if you have more than one child that need to be notified) when the parent's button is pressed:
class FloatingActionButtonNotifier extends ChangeNotifier {
void onFloatingActionButtonPressed() => notifyListeners();
}
class Parent extends StatefulWidget {
const Parent({super.key});
#override
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
final FloatingActionButtonNotifier fabNotifier = FloatingActionButtonNotifier();
#override
Widget build(BuildContext context) {
return Column(
children: [
FloatingActionButton(onPressed: fabNotifier.onFloatingActionButtonPressed),
Child(fabNotifier: fabNotifier),
],
);
}
}
class Child extends StatefulWidget {
const Child({
super.key,
required this.fabNotifier,
});
final FloatingActionButtonNotifier fabNotifier;
#override
State<Child> createState() => _ChildState();
}
class _ChildState extends State<Child> {
void funcToCallFromParent() {
print('child func called from parent');
}
#override
void initState() {
super.initState();
widget.fabNotifier.addListener(funcToCallFromParent);
}
#override
void dispose() {
super.dispose();
widget.fabNotifier.removeListener(funcToCallFromParent);
}
#override
Widget build(BuildContext context) {
return Container();
}
}
You can use GlobalKey for Child, and get the state of Child(StatefulWidget). Then call the child's function in the parent widget.
GlobalKey<MyHomePageState> homePageStateKey = GlobalKey<MyHomePageState>();
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
homePageStateKey.currentState?._incrementCounterAfterOneSecond();
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(key: homePageStateKey, title: 'Flutter Demo Home Page'),
);
}
}
You can use a GlobalKey for that.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _key = GlobalKey<_ChildState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(child: Child(key: _key)),
floatingActionButton: FloatingActionButton(
onPressed: () {
// The current state can be null,
// i.e. there is no widget in the tree because it has been unmounted.
_key.currentState?.funcToCallFromParent();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Child extends StatefulWidget {
const Child({Key? key}) : super(key: key);
#override
State<Child> createState() => _ChildState();
}
class _ChildState extends State<Child> {
void funcToCallFromParent() {
print('child func called from parent');
}
#override
Widget build(BuildContext context) {
return Container();
}
}
See https://api.flutter.dev/flutter/widgets/GlobalKey-class.html
I am having a class instance(SampleData data) as field in the state class of my widget(SecondRoute). The class instance holds in memory while inspecting using the memory profiler. Is that necessary to set null for the instance in dispose() of state class to avoid holding that class object in memory?
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late SecondRoute secondRoute;
#override
void initState() {
secondRoute = const SecondRoute();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ElevatedButton(
child: const Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => secondRoute),
);
},
),
));
}
#override
void dispose() {
super.dispose();
}
}
class SecondRoute extends StatefulWidget {
const SecondRoute({Key? key}) : super(key: key);
#override
State<SecondRoute> createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
SampleData? data;
#override
void initState() {
data = SampleData('John', 28);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Second Page"),
),
body: Center(
child: Column(children: <Widget>[
Row(
children: [Text(data!.name!), Text(data!.age!.toString())],
),
]),
));
}
#override
void dispose() {
data = null;
super.dispose();
}
}
class SampleData {
SampleData(this.name, this.age);
final String? name;
final double? age;
}
#Problem#
Here is my code it is a simple app for practising provider package but when I run it a blank
canvas shows up.
#What I want from it#
Can anyone tell me the reason why nothing is showing up on screen? I have tried creating a fresh project and running it still didn't work.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => const MyApp();
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: MaterialApp(
theme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(
title: const MyText(),
),
body: const Level1(),
),
),
);
}
}
class Level1 extends StatelessWidget {
const Level1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Level2();
}
}
class Level2 extends StatelessWidget {
const Level2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: const [
MyTextField(),
Level3(),
],
);
}
}
class Level3 extends StatelessWidget {
const Level3({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context).data);
}
}
There is a tiny bug in your code... line 4
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
//void main() => const MyApp();
void main() => runApp(const MyApp()); // Update this line.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
...
In flutter land, the runApp function is responsible for inflating the root widget.
Update your main function to call the runApp function and pass in the root widget, MyApp in this case. That should solve the issue.
i want to know how to get updated data when i used FutureProvider.
How can i get and show updated data again when i click the button?
Does it have good way? or should i give up to use FutureProvider?
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: FutureProvider(
create: (_) async => TestDao().findAll(),
child: MyHomePage(title: 'Flutter Demo Home Page')),
);
}
}
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
Widget build(BuildContext context) {
var testData = Provider.of<List<String>>(context);
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
TestDao().insert('test');
});
},
child: Icon(Icons.add),
),
body: Text('$testData'),
);
}
}