I have a custom widget that uses a ListTile. I would like to set the Leading: property to a Checkbox if the Class A is building the widget, but set the Leading property to Null if Class B is building the widget.
Is it possible for the ListTile to know the name of the class that is building it?
Or is there a better way to approach this type of problem?
You can either use the is operator or use obj.runtimeType to check the type of object.
Refer to this link to understand the difference between them.
Here's an example snippet.
class CustomListTile{
var obj;
CustomListTile(this.obj);
void isSameClass(){
// if(obj.runtimeType == Truck)
if(obj is Truck){
print("Building checkbox");
}else{
print("returning Null");
}
}
}
class Chopper{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
class Truck{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
void main(){
Chopper objChop = Chopper();
objChop.test();
Truck objTruck = Truck();
objTruck.test();
}
Would passing a boolean like this do the job for you?
class CustomListTile extends StatelessWidget {
const CustomListTile({Key? key, this.hasLeading = false}) : super(key: key);
final bool hasLeading;
#override
Widget build(BuildContext context) {
return ListTile(
leading: hasLeading ? const Icon(Icons.person) : null,
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
CustomListTile(hasLeading: true), // This one has leading
CustomListTile(), // This one does not
],
);
}
}
Related
This is a case.
I want to use one TimerWidget for 1+ forms.
And i don`t want to save its state in the Store.
So I created it as an Event, and realized like this.
/// Action
class TimeIsOnAction extends AppAction {
TimeIsOnAction(this.timerCounter);
final int timerCounter;
#override
Future<AppState?> reduce() async {
return state.copyWith(timerCounter: Event(timerCounter));
}
}
/// Widget
class TimerWidget extends StatelessWidget {
const TimerWidget({Key? key, required this.timerCounter}) : super(key: key);
final Event<int> timerCounter;
#override
Widget build(BuildContext context) {
final timer = timerCounter.state ?? 0;
// !!!! Consume or Not ???
timerCounter.consume();
return Center(child: Text('$timer'));
}
}
//////////////////////////////////////////////////////////////////////////////
/// Connector
class TimerWidgetConnector extends StatelessWidget {
const TimerWidgetConnector({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _Vm>(
vm: () => _Factory(),
builder: (context, vm) {
return TimerWidget(
timerCounter: vm.timerCounter,
);
},
);
}
}
///
class _Factory extends AppVmFactory {
#override
_Vm fromStore() {
return _Vm(
timerCounter: state.timerCounter,
);
}
}
///
class _Vm extends Vm {
final Event<int> timerCounter;
_Vm({
required this.timerCounter,
}) : super(equals: [timerCounter]);
}
/// Persisting
#override
Future<void> persistDifference(
{AppState? lastPersistedState, required AppState newState}) async {
if (lastPersistedState == null || lastPersistedState != newState) {
return _safeWrapperS(() async {
final json = newState.toJson();
final s = jsonEncode(json);
_saveString(_appStateKey, s);
return;
});
}
}
/// Applying 1
children: [
const Center(child: TimerWidgetConnector()),
Center(child: Text('$isDarkMode')),
/// Applying 2
10.verticalSpace,
const Center(child: TimerWidgetConnector()),
10.verticalSpace,
But! If i consume event in TimerWidget.build after applying - it works only on one Form
If i don't consume - its state automatically persisted with every event changing.
Is there recipe for that case?
I'm trying to change the variable from another stateful class.
class first extends statefulwidget {
bool text = false;
Widget build(BuildContext context) {
setState((){});
return Container(
child: text ? Text('Hello') : Text('check')
);
}
}
class second extends statefulwidget {
Widget build(BuildContext context) {
return Container(
child: IconButton(
onPressed: () {
first fir = first();
setState((){
fir.test = true;
});
}
)
);
}
}
widget shows only check not showing Hello
This is my code...Ignore spelling mistakes and camelcase
Give me the solutions if you know..
If you are trying to access data on multiple screens, the Provider package could help you. It stores global data accessible from all classes, without the need of creating constructors. It's good for big apps.
Here are some steps to use it (there is also a lot of info online):
Import provider in pubspec.yaml
Create your provider.dart file. For example:
class HeroInfo with ChangeNotifier{
String _hero = 'Ironman'
get hero {
return _hero;
}
set hero (String heroName) {
_hero = heroName;
notifyListeners();
}
}
Wrap your MaterialApp (probably on main.dart) with ChangeNotifierProvider.
return ChangeNotifierProvider(
builder: (context) => HeroInfo(),
child: MaterialApp(...),
);
Use it on your application! Call the provider inside any build method and get data:
#override
Widget build(BuildContext context){
final heroProvider = Provider.of<HeroInfo>(context);
return Column {
children: [
Text(heroProvider.hero)
]
}
}
Or set data:
heroProvider.hero = 'Superman';
try to reference to this answer, create function to set boolean in class1 and pass as parameter to class 2 and execute it :
typedef void MyCallback(int foo);
class MyClass {
void doSomething(int i){
}
MyOtherClass myOtherClass = new MyOtherClass(doSomething);
}
class MyOtherClass {
final MyCallback callback;
MyOtherClass(this.callback);
}
I've a question:
In my Widget build(BuildContext context), I want to store a certain value,
final userName = book.owner
(book is the reference to the certain value from Firestore)
But it's done not in the right way to my lack of knowledge. I'd appreciate if someone could guide through that.
Thank you in advance!
Snippet of my code
class BookView extends StatefulWidget {
final Book book;
BookView({Key key, #required this.book}) : super(key: key);
DatabaseMethods databaseMethods = new DatabaseMethods();
var userName;
#override
_BookViewState createState() => _BookViewState(book);
}
class _BookViewState extends State<BookView> {
Book book;
_BookViewState(this.book);
String userName;
#override
void initState() {
userName = book.owner;
super.initState();
}
// final Book book;
createChatroomAndStartConversation({var userName}) {
if (userName != Constants.myName) {
String roomId = getChatRoomId(userName, Constants.myName);
List<String> users = [userName, Constants.myName];
Map<String, dynamic> chatRoomMap = {
"Users": users,
"roomId": roomId,
};
DatabaseMethods().createChatRoom(roomId, chatRoomMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(roomId, userName)),
);
} else {
print("You cannot send msg to your self");
}
}
#override
Widget build(BuildContext context) {
//widget.book;
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
...
FlatButton(
child: Text(
"Get contact with",
style: TextStyle(color: Colors.white),
),
color: Colors.blue,
onPressed: () {
createChatroomAndStartConversation(
userName: userName);
...
}
Snippet of Value not in range: 1
getChatRoomId(String a, String b) {
if (a.substring(0, 1).codeUnitAt(0) > b.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}
}
It's not a good practice to store any data in build() method, because this method is invoked too many times to do the such kind of move. Consider using StatefulWidget to store any state you have in the widget, for the very beginning. When you use this widget, you can define this all in such way:
class YourWidget extends StatefulWidget {
#override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
String userName;
#override
void initState() {
userName = book.owner;
super.initState()
}
#override
Widget build(BuildContext context) {
return Container(child: Text(userName),);
}
}
Here, in initState() you can retrieve value from book and set it to userName. But for more complex and bigger applications, consider using StateManagement solutions and some kind of architectural patterns i.e. Riverpod, Provider, MobX, BLoC.. Because changing the state via setState() method will cause rebuilding whole child widget tree, which could freeze whole UI in complex app.
UPD to 'Snippet of my code':
According to your code, if you are using a 'book' from Widget, not its state - use widget.book, in such way you have access to widget members, because of this you don't need a constructor of state. So, due to these changes, your code might looks like:
class BookView extends StatefulWidget {
final Book book;
BookView({Key key, #required this.book}) : super(key: key);
// You DON'T need this here, because you are retrieving these methods
// inside your state via DatabaseMethods constructor
DatabaseMethods databaseMethods = DatabaseMethods();
#override
_BookViewState createState() => _BookViewState(book);
}
class _BookViewState extends State<BookView> {
String userName;
#override
void initState() {
// Using widget.book to retrieve Book object from state's widget
userName = widget.book.owner;
super.initState();
}
createChatroomAndStartConversation({var userName}) {
if (userName != Constants.myName) {
String roomId = getChatRoomId(userName, Constants.myName);
// Also, it's just a recommendation, try to omit local variables types
// because they are already known with List type (String). Also, this
// all is about chatRoomMap
var users = <String>[userName, Constants.myName];
final chatRoomMap = <String, dynamic>{
"Users": users,
"roomId": roomId,
};
DatabaseMethods().createChatRoom(roomId, chatRoomMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(roomId, userName)),
);
} else {
print("You cannot send msg to your self");
}
}
#override
Widget build(BuildContext context) {
// your widgets here
}
}
UPD 2:
Second trouble and issue with 'Snippet of Value not in range: 1'. I could to reproduce it with given value of 'a' as empty string. So, your function invocation is like getChatRoomId('', 'user123'), because of empty 'userName', substring function can't take values from range [0, 1), so exception is raised.
I am an entry level flutter developer and I've been stuck for a while on this problem, I've tried different things
I am try to make the 'Note Title' (that particular widget), pop out
The app_issue description
then this should show instead
desired result
The two different content under the "My Notes" tab are two Stateful Widgets and the "My Notes" tab is another Stateful Widget on its own
I've tried using a function but it doesn't work
enum MyNoteContent {
staticNote,
dynamicNote,
}
MyNoteContent selectedContent = MyNoteContent.staticNote;
Widget updateMyNotes() {
if (selectedContent == MyNoteContent.staticNote) {
return MyNoteStatic();
} else {
return MyNoteDynamic();
}
}
and then i call the function in the MyNotes Widget
class _MyNotesTabState extends State<MyNotesTab> {
#override
Widget build(BuildContext context) {
return updateMyNotes();
}
}
I trying to update the value in the first content that is shown (in its own Widget), so that when it is pressed, it should change
class _MyNoteStaticState extends State<MyNoteStatic> {
#override
Widget build(BuildContext context) {
return Container(
child: RawMaterialButton(
onPressed: () {
setState(() {
selectedContent = MyNoteContent.dynamicNote;
updateMyNotes();
});
},
but it does not work
Code to Reproduce the Problem
The issue here is that the setState you're calling is for the _MyNoteStaticState class. This means it will only rebuild the MyNoteStatic widget. But in order for the page to change you need to rebuild its parent _MyNotesTabState. So you need to call the setState method of _MyNotesTabState which can be done by passing a callback down from _MyNotesTabState to _MyNoteStaticState.
First, move updateMyNotes & selectedContent into the _MyNotesTabState class since that's the only place they're needed.
Make a new function that rebuilds _MyNotesTabState and changes selectedContent in _MyNotesTabState.
void changeNote() {
setState(() {
selectedContent = MyNoteContent.dynamicNote;
});
}
Pass it down to MyNoteStatic
Widget updateMyNotes() {
if (selectedContent == MyNoteContent.staticNote) {
return MyNoteStatic(changeNote);
} else {
return MyNoteDynamic();
}
}
and modify MyNoteStatic to accept this callback as a parameter
class MyNoteStatic extends StatefulWidget {
final VoidCallback callback;
MyNoteStatic(this.callback);
#override
_MyNoteStaticState createState() => _MyNoteStaticState();
}
Then pass this callback to your button instead of what you currently have:
child: RawMaterialButton(
onPressed: widget.callback,
)
Full relevant code incorporating the above changes:
enum MyNoteContent {
staticNote,
dynamicNote,
}
class MyNotesTab extends StatefulWidget {
#override
_MyNotesTabState createState() => _MyNotesTabState();
}
class _MyNotesTabState extends State<MyNotesTab> {
MyNoteContent selectedContent = MyNoteContent.staticNote;
#override
Widget build(BuildContext context) {
return updateMyNotes();
}
Widget updateMyNotes() {
if (selectedContent == MyNoteContent.staticNote) {
return MyNoteStatic(changeNote);
} else {
return MyNoteDynamic();
}
}
void changeNote() {
setState(() {
selectedContent = MyNoteContent.dynamicNote;
});
}
}
//Static Note
class MyNoteStatic extends StatefulWidget {
final VoidCallback callback;
MyNoteStatic(this.callback);
#override
_MyNoteStaticState createState() => _MyNoteStaticState();
}
class _MyNoteStaticState extends State<MyNoteStatic> {
#override
Widget build(BuildContext context) {
return Container(
child: RawMaterialButton(
onPressed: widget.callback,
...
I am unable to access a public static boolean from a different class, eg. I have a boolean isFull in my StudyjiosListviewScreen class as shown:
class StudyjiosListviewScreen extends StatefulWidget {
#override
_StudyjiosListviewScreenState createState() => _StudyjiosListviewScreenState();
}
class _StudyjiosListviewScreenState extends State<StudyjiosListviewScreen> {
static bool isFull = false;
...
I want to use this boolean isFull in another class JoinStudyjio.
I created an instance of the StudyjiosListviewScreen class in the JoinStudyjio class like this:
StudyjiosListviewScreen listviewScreen = StudyjiosListviewScreen();
But when I try to use the boolean isFull like this:
if (listviewScreen.isFull) {
...
I get an error. I have already imported the file for the StudyjiosListviewScreen class inside the file for the JoinStudyjio class.
This is because StudyjiosListviewScreen and _StudyjiosListviewScreenState are 2 different classes.
The static variable isFull which you are trying to access is of the later one and you are trying to access it by creating an instance of the first one. If it had been a static variable of the class StudyjiosListviewScreen, you could have accessed it without even creating an instance of that class like this StudyjiosListviewScreen.isFull
If I understood your issue correctly, and following the suggestion I made in my comment, here is a code example of sharing a variable and a method to change it's value, down to two classes from a parent class:
class VariableSharing62951032 extends StatefulWidget {
#override
_VariableSharing62951032State createState() => _VariableSharing62951032State();
}
class _VariableSharing62951032State extends State<VariableSharing62951032> {
bool isFull = false;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ClassA62951032(isFull: isFull, swapIsFull: swapIsFull,),
ClassB62951032(isFull: isFull, swapIsFull: swapIsFull,),
],
);
}
void swapIsFull(){
setState(() {
isFull = !isFull;
});
}
}
class ClassA62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassA62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassA62951032State createState() => _ClassA62951032State();
}
class _ClassA62951032State extends State<ClassA62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class A'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
class ClassB62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassB62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassB62951032State createState() => _ClassB62951032State();
}
class _ClassB62951032State extends State<ClassB62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class B'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
Sharing variables and methods between classes it's a huge deal in Flutter.
First of all, you are passing it in the wrong way. That variable is saved in your state widget, which is defined as private.
So, or you define it as public and than you pass a key associated with your state, or you change complitelly approach. I don't like passing keys and it is not good for production, so I will give you a better example using providers:
add provider library to your pubspec.yaml:
provider: ^4.3.1 // Or latest version
Create a class where you can save that value:
class valuesHelper {
//In this class we are storing global, dynamic values
bool _isSeen;
valuesHelper() {
this._isSeen = false;
}
void setValue(bool value) {
this._isSeen = value;
}
bool getValue(){
return this._isSeen;
}
}
Now wrap your main with the provider and pass the valuesHelper();
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Provider(
create: (_) => valuesHelper(),
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
Now call the Provider.of(context) wherever you want.
//Somwhere in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
helper.setValue(true);
//Somwhereelse in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
bool theValueIWant = helper.getValue();
If you have asynchronous stuff and huge state managment Blocs are even better and fancier, but for this kind of things Providers are more than enough.