I am trying to write a Typehead package. In case of that, I need to pass functions with generic parameters but I am getting a type error.
Here is my simplified code:
Models:
class BaseModel {
BaseModel({required this.title});
String title;
}
class SearchModel extends BaseModel {
SearchModel({required super.title, required this.myText});
String myText;
}
TypeHead Class:
class HorizontalTypeHead<T extends BaseModel> extends StatefulWidget {
const HorizontalTypeHead({
Key? key,
required this.onLookup,
required this.onSelected,
}) : super(key: key);
final Future<Iterable<T>> Function(String value) onLookup;
final Function(T model) onSelected;
#override
State<HorizontalTypeHead> createState() => _HorizontalTypeHeadState<T>();
}
class _HorizontalTypeHeadState<T extends BaseModel> extends State<HorizontalTypeHead> {
Iterable<T> _data = [];
List<Widget> renderColumn(BuildContext context) {
List<Widget> list = [
TextField(
onChanged: (String? val) async {
if (val != null) {
_data = await widget.onLookup(val) as Iterable<T>;
setState(() {});
}
},
),
];
if (_data.isNotEmpty) {
list.add(
SizedBox(
height: 100,
child: ListView(
scrollDirection: Axis.horizontal,
children: _data
.map((e) => ResultWidget<T>(
model: e, onSelected: widget.onSelected))
.toList(),
),
),
);
}
return list;
}
#override
Widget build(BuildContext context) {
return Column(children: renderColumn(context));
}
}
Result Widget:
class ResultWidget<T extends BaseModel> extends StatelessWidget {
const ResultWidget({required this.model, required this.onSelected, Key? key})
: super(key: key);
final T model;
final Function(T selected) onSelected;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () => onSelected(model),
child: Container(
color: Colors.red,
height: 100,
width: 100,
child: Text(model.title),
));
}
}
My main goal is to access data in SearchModel (myText field). But when I am assigning SearchModel as type, I get a type error.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Future<Iterable<SearchModel>> onLookup(String pattern) async {
return [SearchModel(title: "title", myText: "myText")];
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: HorizontalTypeHead<SearchModel>(
onSelected: ((model) => print(model.myText)), //I need to get myText
onLookup: onLookup,
),
));
}
}
_TypeError (type '(SearchModel) => void' is not a subtype of type '(BaseModel) => dynamic')
I would like to ask what I need to do for accessing SearchModel without any error from main class. Thanks in advance!
I just made generic my state class and solved it :)
class _HorizontalTypeHeadState<T extends BaseModel>
extends State<HorizontalTypeHead<T>>
instead of
class _HorizontalTypeHeadState<T extends BaseModel>
extends State<HorizontalTypeHead>
Related
I have some some stless wrap widget:
class SomeWrap extends StatelessWidget {
// it's work, but i can put any widget, but I want only widget form class MyChoises
Widget MyValue
const SomeWrap({
required this.MyValue,
Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
// Here some settings for UI
// here my value as a widget
child: MyValue;
);
}
}
Here class MyChoises, that return couple widgets:
abstract class StatusTextOrder {
Text processing = Text('Processing',style: TextStyle(color:Colors.Yellow)));
Text delivered = Text('Delivered',style: TextStyle(color:Colors.Green)));
IconButton canceled = IconButton(icon: Text('Canceled', onPressed: ()=>{}))
}
What the correct way to use this "choices" for a value ?
usage:
SomeWrap(MyValue: StatusTextOrder.delivered)
Now into MyValue I can put any Widget, its not that im looking for.
I tried to use none abstract class, and put StatusTextOrder or Widget , but all of this gives me an errors.
Someone said that it will work :
import 'package:flutter/material.dart';
class StatusTextOrder {
static final processing =
Text('Processing', style: TextStyle(color: Colors.yellow));
static final delivered =
Text('Delivered', style: TextStyle(color: Colors.green));
static final canceled = IconButton(
onPressed: () {},
icon: Icon(
Icons.cancel,
color: Colors.red,
));
}
class Wrapper extends StatelessWidget {
StatusTextOrder widget;
Wrapper({Key? key, required this.widget}) : super(key: key);
#override
Widget build(BuildContext context) {
return Placeholder(
child: widget,
);
}
}
class ErrorPage extends StatelessWidget {
const ErrorPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(child:Row(
children: [
Wrapper(widget: StatusTextOrder.delivered),
],
)),
);
}
}
no, it gives errors:
The argument type 'StatusTextOrder' can't be assigned to the parameter type 'Widget?'.
The argument type 'Text' can't be assigned to the parameter type 'StatusTextOrder'.
As comment section included desire behavior, It can be
wrapper class,
//wrapper class
class SomeWrap extends StatelessWidget {
final StatusTextOrder statusTextOrder;
const SomeWrap({required this.statusTextOrder, Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return statusTextOrder;
}
}
//base class
abstract class StatusTextOrder extends Widget {
const StatusTextOrder({super.key});
}
/// concrete class
class Processing extends StatelessWidget implements StatusTextOrder {
const Processing({super.key});
#override
Widget build(BuildContext context) {
return const Text('processing');
}
}
And you need to use like
SomeWrap(statusTextOrder: Processing());
Also it can be
abstract class StatusTextOrder extends StatelessWidget {
const StatusTextOrder({super.key});
}
class Processing extends StatusTextOrder {
const Processing({super.key});
#override
Widget build(BuildContext context) {
return const Text('processing');
}
}
You can create another class with theses concrete Class as static variable and pass like old part.
old:
To use like StatusTextOrder.delivered, you need to make those variable as statics,
abstract class StatusTextOrder {
static Text processing = Text('Processing');
static Text delivered = Text('Delivered');
static IconButton canceled = IconButton(icon: Text('Canceled'), onPressed: () => {});
}
class SomeWrap extends StatelessWidget {
final Widget MyValue;
const SomeWrap({required this.MyValue, Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(child: MyValue);
}
}
You can find more about class-variables-and-methods
I am new to flutter and I am trying to use get_it package for DI. I want to understand how can I replace my dependance on the Inherited Widget with get_it.
My code looks like this:
I have a locator file which initialises the locator instance and its setup.
locator.dart file:
final locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton<ExampleProvider>(() => ExampleProvider());
}
example_provider.dart:
class ExampleProvider with ChangeNotifier {
bool _value = false;
bool get value => _value;
set setValue(bool newValue) {
_value = newValue;
notifyListeners();
}
}
This is the HomePage where provide the ChangeNotifierProvider with ExampleProvider to the child widget.
home_page.dart file:
class HomePage extends StatefulWidget {
final String title;
const HomePage({Key? key, required this.title}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _exampleProvider = locator.get<ExampleProvider>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
key: Key('issue_list_screen_column'),
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ChangeNotifierProvider(
create: (context) => _exampleProvider,
child: ExampleWidget(
key: Key('example_widget'),
),
),
],
),
appBar: AppBar(
title: Text(widget.title),
),
);
}
}
This is the ExampleWidget where I want to use the locator instead of Provider.of<ExampleProvider>(context)
example_widget.dart file:
class ExampleWidget extends StatefulWidget {
const ExampleWidget({
Key? key,
}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
#override
Widget build(BuildContext context) {
// How do I use locator over here with context??
final _exampleProvider = Provider.of<ExampleProvider>(context);
return Switch(
value: _exampleProvider.value,
onChanged: (newValue) {
themeProvider.setValue = newValue;
},
);
}
}
So, I want to use locator for the line final _exampleProvider = Provider.of<ExampleProvider>(context); in ExampleWidget. How can I do that?
It does not work like this: final _exampleProvider = locator.get<ExampleProvider>();
try this
final _exampleProvider = locator.get<ExampleProvider>();
When I am invoking setState in child stateful widget. it is showing an error or warning. Is there any way to call Stateful widget inside Stateful widget without causing an error or any good way to do the same?
Here is my sample code :
parent.dart
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
var title = "Parent";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
children: <Widget>[
Text(title),
Child(init:true), // <-- Calling Child Widget
],
),
),
);
}
}
child.dart
class Child extends StatefulWidget {
final bool init; // <- Showing warning on removing 'final'
// This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final: Child.init
Child({
Key? key,
required this.init,
}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Container(
color: widget.init ? Colors.red : Colors.blue,
child: TextButton(
onPressed: () {
setState(
() {
// widget.init = false;
// want to change 'wiget.init' but its final
// removing final causing warning
},
);
},
child: Text("Click me"),
),
);
}
}
You can pass function that changes init variable.
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
var title = "Parent";
var init = true;
void setInitFalse(){
setState((){
init = false;
})
}
bool getInit(){
return init;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
children: <Widget>[
Text(title),
Child(init: getInit(), setInitFalse: setInitFalse()),
],
),
),
);
}
}
And then in child
class Child extends StatefulWidget {
final Function init; // <- Showing warning on removing 'final'
// This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final: Child.init
final Function setInitFalse;
Child({
Key? key,
required this.init,
required this.setInitFalse
}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Container(
color: widget.init() ? Colors.red : Colors.blue,
child: TextButton(
onPressed: () {
widget.setInitFalse();
},
child: Text("Click me"),
),
);
}
}
am using something like that
class OpenPage extends StatefulWidget {
#override
_OpenPageState createState() => _OpenPageState();
}
class _OpenPageState extends State<OpenPage> {
int id= 0;
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
classOne(), //id changed here
classTwo(), //here data depends on id
],
);
}
where classOne and classTwo are StatefulWidget .
what am tring to do when i change id value in classOne i want it to change the classTwo where the data in the classTwo depends on the id that change in classOne .
how could i setState classTwo when the id in classOne change ?
You could pass the function of manipulating the id as an argument of WidgetOne. In this example WidgetOne and WidgetTwo are StatelessWidget but it should also work for StatelessWidget
class OpenPage extends StatefulWidget {
#override
_OpenPage createState() => _OpenPage();
}
class _OpenPage extends State<OpenPage > {
int id = 0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
WidgetOne(
onPressed: () {
setState(() {
id++;
});
},
),
WidgetTwo(
id: id,
),
],
);
}
}
class WidgetOne extends StatelessWidget {
final VoidCallback onPressed;
const WidgetOne({Key key, this.onPressed}) : super(key: key);
#override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.add),
onPressed: onPressed,
);
}
}
class WidgetTwo extends StatelessWidget {
final int id;
const WidgetTwo({Key key, this.id}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(id.toString());
}
}
Reproducible Code:
void main() => runApp(MaterialApp(home: CountInheritedWidget(child: HomePage())));
class CountInheritedWidget extends InheritedWidget {
CountInheritedWidget({Widget child}) : super(child: child);
final Map<String, int> _map = {"count": 0};
// getter
int get value => _map["count"];
// setter
set value(int x) => _map["count"] = x; // is there anything like setState here?
#override
bool updateShouldNotify(CountInheritedWidget oldCounter) => true;
static CountInheritedWidget of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>();
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextWidget(),
ButtonWidget(),
],
),
),
);
}
}
class TextWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
int count = CountInheritedWidget.of(context)?.value ?? -1;
return Text("Count = $count");
}
}
class ButtonWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Increment"),
onPressed: () {
CountInheritedWidget counter = CountInheritedWidget.of(context);
int count = counter?.value ?? -1;
counter.value = ++count;
},
);
}
}
I'd like to update the value of count from ButtonWidget, I am sure it is getting updated in the CounterInheritedWidget class but it isn't reflecting on the screen. How can I call setState or something like that from InheritedWidget?
Any help will be appreciated, I am newbie to Flutter and Dart so having tough time in solving this kind of issue. Thank you and have a great day.
Note: I am not looking for some plugins like Provider, ScopedModel, Redux for this kinda work.
InheritedWidgets cannot do that. They are completely immutable with no mechanism for triggering updates.
If you want to emit updates, you will have to combine your InheritedWidget with a StatefulWidget, typically done in such way:
class MyWidget extends StatefulWidget {
const MyWidget({Key key, this.child}) : super(key: key);
final Widget child;
#override
MyState createState() => MyState();
}
class MyState extends State<MyWidget> {
String name;
int age;
#override
Widget build(BuildContext context) {
return MyInherited(
name: name,
age: age,
child: widget.child,
);
}
}
Where MyInheritedWidget is:
class MyInherited extends InheritedWidget {
MyInherited({
Key key,
this.name,
this.age,
Widget child,
}) : super(key: key, child: child);
final String name;
final int age;
#override
bool updateShouldNotify(MyInherited oldWidget) {
return name != oldWidget.name && age != oldWidget.age;
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('age', age));
properties.add(StringProperty('name', name));
}
}
Yup. That's verbose. Which is why provider exists.
Here is a complete example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyStateful(
child: Column(
children: <Widget>[
MyCounter(),
MyButton(),
],
),
),
),
);
}
}
// MyStateful and MyInherited together act like a Provider
class MyStateful extends StatefulWidget {
const MyStateful({Key? key, required this.child}) : super(key: key);
final Widget child;
#override
MyState createState() => MyState();
}
class MyState extends State<MyStateful> {
int _count = 0;
void increment() {
setState(() {
_count += 1;
});
}
#override
Widget build(BuildContext context) {
return MyInherited(
count: _count,
increment: this.increment,
child: widget.child,
);
}
}
// Whenever state values are changes a new MyInherited is created
// with new parameters.
class MyInherited extends InheritedWidget {
MyInherited({
Key? key,
required this.count,
required this.increment,
required Widget child,
}) : super(key: key, child: child);
final int count;
final void Function() increment;
#override
bool updateShouldNotify(MyInherited oldWidget) {
return count != oldWidget.count;
}
static MyInherited of(BuildContext context) {
final MyInherited? result =
context.dependOnInheritedWidgetOfExactType<MyInherited>();
assert(result != null, 'No MyInherited found in context');
return result!;
}
}
class MyCounter extends StatelessWidget {
const MyCounter({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text('Count: ${MyInherited.of(context).count}');
}
}
class MyButton extends StatelessWidget {
const MyButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton(
onPressed: () {
MyInherited.of(context).increment();
},
child: const Text('Increment'),
);
}
}