How to get a List from another class - flutter

I have a characterList class that has a final List <Character> character field;
How can I access the character from the SWMain class?
SWMain class:
class _SWMainState extends State<SWMain> {
Icon customIcon = Icon(Icons.search);
static Text titleText = Text("Star Wars API");
Widget customSearchBar = titleText;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar()
body: FutureBuilder<List<Character>>(
future: fetchCharacters(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? CharacterList(character: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
),
);
}
}
characterList class:
class CharacterList extends StatelessWidget {
final List<Character> character;
CharacterList({Key key, this.character}) : super(key: key);
...
}

CharacterList({Key key, this.character}) : super(key: key);
this is the constructor.
You are using a stateless widget so you can directly access via variable name which is character in your case.
if you are using a stateful widget so you have to access it via widget.character
Thanks.

Related

Get an Integer with Flutter from the Cloud Firestore

I am new at Flutter and I try to build an App. I want to Display an double on a Textwidget from Cloud Firestore.
My CloudFirestore look like this:
How can I read the double of 200 in a TextWidget and Displays all the time the new value?
I have create a function to add Integer on the specific logged In User Mail what looks like this:
geldNew = _firestore.collection('guthaben').doc(loggedInUser?.email).set({
'geld': geld,
});
You can use a StreamBuilder and the snapshots to stream the value, so when there's a change, the UI gets updated automatically like so:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('guthaben')
.doc("loggedInUser?.email")
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const CircularProgressIndicator();
}
return Text((snapshot.data!.data() as Map)["geld"].toString());
},
),
),
);
}
}
If you want to get the value just once, you can use a FutureBuilder like so:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance
.collection('guthaben')
.doc("loggedInUser?.email")
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const CircularProgressIndicator();
}
return Text((snapshot.data!.data() as Map)["geld"].toString());
},
),
),
);
}
}

how to display the value that came from valueNotifier?

I have a valueNotifier that generates a list of events and takes a random string every 5 seconds and sends it to the screen. It lies in inheritedWidget. How can I display in the ListView the event that came with the valueNotifier? What is the correct way to print the answer?
My code:
class EventList extends StatelessWidget {
const EventList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return EventInherited(
child: EventListScreen(),
);
}
}
class EventListScreen extends StatefulWidget {
const EventListScreen({Key? key}) : super(key: key);
#override
State<EventListScreen> createState() => _EventListScreenState();
}
class _EventListScreenState extends State<EventListScreen> {
#override
Widget build(BuildContext context) {
final eventNotifier = EventInherited.of(context).eventNotifier;
return Scaffold(
appBar: AppBar(
title: const Text('Event List'),
centerTitle: true,
),
body: Container(
padding: const EdgeInsets.all(30),
child: ValueListenableBuilder(
valueListenable: eventNotifier,
builder: (BuildContext context, List<String> value, Widget? child) {
return ListView(
children: [
],
);
},
),
),
);
}
}
class EventNotifier extends ValueNotifier<List<String>> {
EventNotifier(List<String> value) : super(value);
final List<String> events = ['add', 'delete', 'edit'];
final stream = Stream.periodic(const Duration(seconds: 5));
late final streamSub = stream.listen((event) {
value.add(
events[Random().nextInt(4)],
);
});
}
class EventInherited extends InheritedWidget {
final EventNotifier eventNotifier = EventNotifier([]);
EventInherited({required Widget child}) : super(child: child);
static EventInherited of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType()!;
}
#override
bool updateShouldNotify(EventInherited oldWidget) {
return oldWidget.eventNotifier.streamSub != eventNotifier.streamSub;
}
}
If you have correct value, you can return listview like this:
return ListView.builder(
itemCount: value.length,
itemBuilder: (context, index) {
return Text(value[index]);
},
);
After having a quick look at ValueNotifier,
It says the following:
When the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==, this class notifies its listeners.
In your case, the value is an array. By adding items to the array, it wont recognise a change.
Also see other Stacko post.
Try something like:
value = [...value].add(...)

Widget not rebuilding after changeNotifier called

I have a provider with an int variable currentPage that defines the initial page of a PageView. I have this because I want to change the currentPage with widgets that far under the tree, or descendent widgets. I've set up everything correctly, but when changeNotifier is called, the page doesn't change.
Here's the provider class-
class CurrentPageProvider with ChangeNotifier{
int? currentPage;
CurrentPageProvider({this.currentPage});
changeCurrentPage(int page) {
currentPage = page;
notifyListeners();
}
}
To use it, I've wrapped my MaterialWidget with a MultiProvider as such-
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => CurrentPageProvider(currentPage: 0))
],
child: MaterialApp(
title: "Test",
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
primaryColor: yellowColor,
),
home: const ResponsiveRoot(),
),
);
}
}
And here's the widget where the child should rebuild, but isn't-
class ResponsiveRoot extends StatelessWidget {
const ResponsiveRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
int currentPage = Provider.of<CurrentPageProvider>(context).currentPage!;
print("CurrentPageUpdated");
return LayoutBuilder(
builder: ((context, constraints) {
if (constraints.maxWidth > kWebScreenWidth) {
return const WebscreenLayout();
} else { //The page view is here
return MobileScreenLayout(
currentPage: currentPage,
);
}
}),
);
}
}
Upon debugging, I've found out that "CurrentPageUdated" gets printed when I'm calling the changeCurrentPage. However, the initState of the MobileScreenLayout doesn't get called (This widget has the pageView)
How do I fix this? Thanks!
in order to update the state of the the app you need to use Consumer widget.
Consumer<Your_provider_class>(
builder: (BuildContext context, provider_instance, widget?){
},
child: any_widget, but not neccessary,
)
The problem seems to be that even though your Provider.of mechanism needs to listen to changes, it does not.
What you can do is, do the recommended way on the documentation and you can either use the watch extension function or use Consumer or Selector widgets.
Here is an example on how to do it with your example with a Selector.
For more information read about Selector here
class ResponsiveRoot extends StatelessWidget {
const ResponsiveRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Selector<CurrentPageProvider, int>(
selector: (context, provider) => provider.currentPage!,
builder: (context, currentPage, child) {
print("CurrentPageUpdated");
return LayoutBuilder(
builder: ((context, constraints) {
if (constraints.maxWidth > kWebScreenWidth) {
return const WebscreenLayout();
} else {
//The page view is here
return MobileScreenLayout(
currentPage: currentPage,
);
}
}),
);
},
);
}
}

Flutter streambuilder does not update a statefulwidget or statelesswidget

I'm trying to build a streambuilder.
My problem is that the streambuilder does not update other statefulWidgets inside the streambuilder widget.
Here my code:
class ChatPage extends StatefulWidget {
//pass parm
var chat_object;
ChatPage(this.chat_object, {Key? key}) : super(key: key);
#override
_ChatPage createState() => _ChatPage();
}
class _ChatPage extends State<ChatPage> {
//pass parm
var chat_object;
_ChatPage(this.chat_object);
#override
Widget build(BuildContext context) {
// Full screen width and height
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
// Height (without SafeArea)
var padding = MediaQuery.of(context).padding;
double height1 = height - padding.top - padding.bottom;
// Height (without status bar)
double height2 = height - padding.top;
// Height (without status and toolbar)
double height3 = height - padding.top - kToolbarHeight;
//scaffold
return Scaffold(
appBar: customAppBarGoBack('Chat', context),
body: LimitedBox(
maxHeight: height3,
child: Stack(
children: [
StreamBuilder<dynamic>(
stream: FirebaseFirestore.instance
.collection('chat')
.doc('chat_messages')
.collection(chat_object.chat_message_id[0].trim())
.orderBy('message_time')
.snapshots(),
builder: (
BuildContext context,
AsyncSnapshot<dynamic> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
//chat room object list
var chat_room_object =
load_object(snapshot.data, chat_object).first;
//chat message object list
var message_object =
load_object(snapshot.data, chat_object).last;
print('message_object: ' + message_object.toString());
return Test(chat_room_object, message_object);
} else {
return const Text('Empty data');
}
}
},
),
],
),
),
drawer: CustomSideBar(context),
);
}
}
//test
class Test extends StatefulWidget {
//pass parm
var chat_room_object;
var message_object;
Test(this.chat_room_object, this.message_object, {Key? key})
: super(key: key);
#override
_Test createState() => _Test(chat_room_object, message_object);
}
class _Test extends State<Test> {
//pass parm
_Test(this.chat_room_object, this.message_object);
var chat_room_object;
var message_object;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: message_object.length,
itemBuilder: (context, i) {
return Text(message_object[i].message.toString());
},
);
}
}
If I build a list inside the streambuilder the list will be update.
But if I build the list inside a new widget and pass the values to list the widget will be not update.
Anyone can give me a simple example how I can update the values in a statefulwidget which is contain in a streambuilder?
Instead of passing parameters in the constructor of state of Test widget, can you try to access them directly from the widget.Example as below.
//test
class Test extends StatefulWidget {
//pass parm
var chat_room_object;
var message_object;
Test(this.chat_room_object, this.message_object, {Key? key})
: super(key: key);
#override
_Test createState() => _Test();
}
class _Test extends State<Test> {
//pass parm
_Test();
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.message_object.length,
itemBuilder: (context, i) {
return Text(widget.message_object[i].message.toString());
},
);
}
}

Flutter - How to make a variable accessible to a child widget

i've created a StatelessWidget named FirestoreStreamBuilder that create a Stream and return a child.
class FirestoreStreamBuilder extends StatelessWidget {
FirestoreStreamBuilder({#required this.collectionReference, #required this.child, #required this.noDataChild});
final String collectionReference;
final Widget child;
final Widget noDataChild;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection(collectionReference).snapshots() ,
builder: (context, snapshot) {
if (!snapshot.hasData) return noDataChild;
return child;
},
);
}
}
I need to make snapshot accessible from child. How can i do that?
Example:
FirestoreStreamBuilder(
collectionReference: 'collection',
child: Text('I want to acces to snapshot here $snapshot'), //How can I access snapshot from here?
noDataChild: Container(),
)
Create custom callback
typedef OnDataReceived = void Function(DocumentSnapShot);//or your return type
class FirestoreStreamBuilder extends StatelessWidget {
FirestoreStreamBuilder({#required this.collectionReference, #required this.child,
#required this.noDataChild, #required this.onDataReceived});
final String collectionReference;
final Widget child;
final Widget noDataChild;
final OnDataReceived onDataReceived;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection(collectionReference).snapshots() ,
builder: (context, snapshot) {
if (!snapshot.hasData) {
onDataReceived(snapshot);
return noDataChild;
}
return child;
},
);
}
}
Access snapshot in widget
FirestoreStreamBuilder(
collectionReference: 'collection',
child: Text('Hurray you got snapshot !'),
noDataChild: Container(),
onDataReceived:(snapshot){
//TODO your task with snapshot
}
)