I've got a model which I simplify for the purpose of this question
MyModel(a:0, b:0, c:0) //a b c are integers
Now I need to generate buttons from this model so I've created a new class which takes that model as a parameter and generates buttons
class Panel extends StatelessWidget {
final MyModel model;
const Panel({this.model});
List<Widget> _buildContent() {
final list = [
{'a': model.a},
{'b': model.b},
{'c': model.c}
];
List<Widget> l = [];
list.forEach((element) {
l.add(FlatButton(child: Text(element.keys.first), onPressed: () {}));
});
return l;
}
#override
Widget build(BuildContext context) {
return Container(
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _buildContent(),
));
}
}
and in my main class, I'm just calling it like this
Panel(model: myModel);
I'm trying to find the neatest way to handle presses of these buttons, each button should update its element at +1 on each press and send the updated model back to the main class
You need for your Panel to provide a callback when the model changes. Then you can change the model used in your application. Note the "onChange" callback added to the Panel that in the App calls setState to actually change the model.
Example:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class Model {
final int a;
final int b;
final int c;
const Model({this.a, this.b, this.c});
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Model model = Model(a: 1, b: 2, c: 3);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: Text('Current Model: a:${model.a}, b:${model.b}, c: ${model.c}')),
body: Center(
child: Panel(
model: model,
onChange: (changedModel) => setState(() => model = changedModel)),
),
),
);
}
}
class PanelButton {
final String text;
final Model Function() onClick;
const PanelButton(this.text, this.onClick);
}
class Panel extends StatelessWidget {
final Model model;
final void Function(Model) onChange;
const Panel({#required this.model, this.onChange});
List<Widget> _buildContent() {
final list = [
PanelButton('a', () => Model(a: model.a + 1, b: model.b, c: model.c)),
PanelButton('b', () => Model(a: model.a, b: model.b + 1, c: model.c)),
PanelButton('c', () => Model(a: model.a, b: model.b, c: model.c + 1)),
];
return list
.map((x) => FlatButton(
child: Text(x.text), onPressed: () => _raiseOnChange(x.onClick())))
.toList();
}
void _raiseOnChange(Model model) {
if (onChange != null) {
onChange(model);
}
}
#override
Widget build(BuildContext context) {
return Container(
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _buildContent(),
));
}
}
As far as my knowledge is concerned, what you can do is the following:
Rather than following model thing, you can make a Global Variable, which has a dictionary/hasmap. Make a file named as globals.dart and do this
/*
This is the primary util for maintaining
all the Global values/variables
*/
library globals;
//This is your variable
Map<String, dynamic> mydata = {'a':0, 'b':0, 'c':0};
Now, you can access global value anywhere in any file, by just importing the globals.dart file like import '{path}/globals.dart'
Now in your Panel, we can access it, and do the increment of the item as per your will
import '{path}/globals.dart' as globals
class Panel extends StatelessWidget {
List<Widget> _buildContent() {
List<Widget> l = [];
// No list declaration, direct items access
globals.mydata.forEach((key, value) {
l.add(
FlatButton(
child: Text(key),
onPressed: (key) {
//on pressed you just increment when it is hit for the key elem
globals.mydata[key] += 1;
}
));
});
return l;
}
#override
Widget build(BuildContext context) {
return Container(
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _buildContent(),
));
}
}
Final step, now when you hit on the button, you get updated HasMap, which is globally accessible, so you can call the Panel(), no params required. And when you want to check the updated value just print it
import '{path}/globals.dart' as globals
Panel()
// This will give out the updated data if the button is hit else {'a': 0, 'b': 0, 'c': 0}
print(globals.mydata)
This is the cleanest way I can think for you in this case. I hope that would work out for you as well :) Let me know, I would be happy if that will work out :)
Related
I have a Stateless-Provider widget along with its ChangeNotifier-model. Inside the Provider, there is a Stateful widget. When notifyListeners is called, all widgets in the stateless widget get updated, except the Stateful one. What am I missing here, and how do I go about it? Providing a simplified example here: Upon pressing the button, the expected result is First: The value is 1st, but the actual output is First: The value is 2nd
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Model extends ChangeNotifier {
final List<ListElement> elements;
Model({required this.elements});
void add() {
elements.insert(0, ListElement(name: "First", value: "1st"));
notifyListeners();
}
}
class ListElement {
final String name;
var value;
ListElement({required this.name, required this.value});
}
class ValueWidget extends StatefulWidget {
final String value;
ValueWidget({required this.value});
#override
State<StatefulWidget> createState() => _ValueWidget(value: value);
}
class _ValueWidget extends State<ValueWidget> {
String value;
_ValueWidget({required this.value});
#override
Widget build(BuildContext context) {
return Text("The value is ${value}.");
}
}
class StatelessPage extends StatelessWidget {
final model = Model(elements: [
ListElement(name: "Second", value: "2nd"),
ListElement(name: "Third", value: "3rd")]);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => model,
child: ConsumerWidget())
);
}
}
class ConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Model>(builder: (context, model, _) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
child: Column(
children: [Column(
children: model.elements.map((element) {
return Row(children: [
Text("${element.name}: "),
ValueWidget(value: element.value)
]);
}).toList(),
),
TextButton(onPressed: model.add,
child: Text("Add element to beginning")),
],
),
),
);
});
}
}
Please consider that this is simplified version of my production code, and changing the whole Provider class to a Stateful one would be difficult.
Edit: Thanks Aimen for showing the path. What finally worked was using only the index of the list elements in the Stateful wiget (ValueWidget). And fetch the data from the model. I think the reason for this is that if the Stateful-widget in independece is not affected, it will not rebuild. We need to affect the build part of the widget. Pasting the changed working code.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Model extends ChangeNotifier {
final List<ListElement> elements;
Model({required this.elements});
void add() {
elements.insert(0, ListElement(name: "First", value: "1st"));
notifyListeners();
}
}
class ListElement {
final String name;
var value;
ListElement({required this.name, required this.value});
}
class ValueWidget extends StatefulWidget {
final int ind;
final Model model;
ValueWidget({required this.ind, required this.model});
#override
State<StatefulWidget> createState() => _ValueWidget(
ind: ind, model: model);
}
class _ValueWidget extends State<ValueWidget> {
final int ind;
final Model model;
_ValueWidget({required this.ind, required this.model});
#override
Widget build(BuildContext context) {
// Can also use Provider like this so that it does not need to be passed
// final model = Provider.of<Model>(context, listen: true);
// This is the part because of which widget is getting rebuilt
final element = model.elements[ind];
return Text("The value is ${element.value}.");
}
}
class StatelessPage extends StatelessWidget {
final model = Model(
elements: [
ListElement(name: "Second", value: "2nd"),
ListElement(name: "Third", value: "3rd")]
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => model,
child: ConsumerWidget())
);
}
}
class ConsumerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<Model>(builder: (context, model, _) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
child: Column(
children: [Column(
children:
model.elements.asMap().entries.map((ele) {
return Row(children: [
Text("${ele.value.name}: "),
ValueWidget(ind: ele.key, model: model),
]);
}).toList(),
),
TextButton(onPressed: model.add,
child: Text("Add element to beginning")),
],
),
),
);
});
}
}
you are not implementing provider in the stateful widget you are just passing a value through a parameter you need to call a provider and set the listen to true
inside the statful widget
like
var model = Model.of(context, listen = true);
List elements = model.elements;
here the elements variable will change when the elements in the provider will have a new value
Im sorry if the title seems off, I couldn't find a better way to frame it. I have a grid of containers on the screen, and I want to be able to draw on the screen selecting and dragging across the screen. I read about the GestureDetector class but it only detects gesture that starts in one widget, I can do a onPanDown, onPanUpdate,onPanEnd but that just gives me the co-ordinates of the cursor, and I didn't feel like it was the most elegant way to do it.(I might be wrong). The Block Class:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class Block extends StatefulWidget {
#override
_BlockState createState() => _BlockState();
}
class _BlockState extends State<Block> {
Color boxColor = Colors.white;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (boxColor == Colors.blueGrey[900])
boxColor = Colors.white;
else
boxColor = Colors.blueGrey[900];
setState(() {});
},
child: Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: boxColor,
border: Border.all(
color: Colors.grey,
width: 1,
)),
),
);
}
}
PathFinding Class: (Painting the blocks in a grid):
import 'package:flutter/material.dart';
import 'Block.dart';
class PathFinding extends StatefulWidget {
#override
_PathFindingState createState() => _PathFindingState();
}
class _PathFindingState extends State<PathFinding> {
#override
Widget build(BuildContext context) {
List<List<Widget>> grid = [
...List.generate(
40, (index) => [...List.generate(40, (index) => Block())])
];
return Scaffold(
body: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
...grid.map((e) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [...e],
))
],
),
);
}
}
Just pass a callback as a parameter from your _PathFindingState, whenevr you are creating a Block.
First, add 2 extra params to your Block that can be passed while creating it.
class Block extends StatefulWidget {
final void onTap; // The function from the parent to be called
final int id; // An id that is unique to this Block
Block({ this.onTap, this.id });
#override
_BlockState createState() => _BlockState();
}
Then, in your _BlockState, whenever a tap is detected, call the new function to inform the Block class, which will then inform the _PathFindingState class.
InkWell(
onTap: () {
if (boxColor == Colors.blueGrey[900])
boxColor = Colors.white;
else
boxColor = Colors.blueGrey[900];
setState(() {});
widget.onTap(widget.id); // This line will call the `onTap` function that is present in the `Block`
},
Finally, in your _PathFindingState,
class _PathFindingState extends State<PathFinding> {
void onTap (int id) {
// A Block with `id` = id has been tapped,
}
#override
Widget build(BuildContext context) {
List<List<Widget>> grid = [
...List.generate(
40, (index) => [...List.generate(40,
(index) => Block(id: index, onTap: onTap) // Pass index as id and the onTap function
)])
];
This architecture can be followed for any Gesture that has been detected on any Block and you will receive the callback in the _PathFindingState class and you can do whatever you want with it.
As shown in the image, I'm trying to have a list of dice where I can add or delete a die. I've tried StateProvider, ChangeNotifier, and StateNotifier. Each one doesn't seem to work as I expect it to. I'm trying to make a provider that contains a list of dieWidgets, but I can't figure out how to remove a specific die when I longpress on it. The image shows a popup menu to delete it, that's the long-term goal, but just a longpress delete would be good for now. Thoughts on how to approach this?
Code
main.dart
class DiceNotifier extends ChangeNotifier {
List<DieWidget> dice = [];
void add() {
dice.add(DieWidget());
notifyListeners();
}
void removeDie(int id) {
// FIXME: Unable to delete a die based on id
print(id);
notifyListeners();
}
}
final diceProvider = ChangeNotifierProvider((_) {
return DiceNotifier();
});
class MyHomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final dice = watch(diceProvider).dice;
return Scaffold(
appBar: AppBar(
title: Text("Dice"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
...dice,
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(diceProvider).add();
},
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
die_widget.dart
class DieWidget extends StatefulWidget {
#override
_DieWidgetState createState() => _DieWidgetState();
}
class _DieWidgetState extends State<DieWidget> {
int value = 0;
int id = 0;
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
'$value',
),
onPressed: () {
setState(() {
value++;
id++;
});
// context.read(dieProvider).increment();
},
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDie(id);
// print(this.value);
},
);
}
}
One solution would be to define a parameter value in the DiceWidget class:
class DiceWidget extends StatefulWidget {
const DiceWidget({ Key key, this.value }) : super(key: key);
int value;
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
And access this data from the DiceWidget:
class DiceWidget extends StatefulWidget {
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
class _DiceWidgetState extends State<DiceWidget> {
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
widget.value.toString() ?? '',
),
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDice(widget.value);
// print(widget.value);
},
);
}
}
In the DiceNotifier class, I'd recommend to implement the dices array as a List<int>:
List<int> dices = [];
Therefore, the addDice() and removeDice() functions will be, respectively:
class DiceNotifier extends ChangeNotifier {
List<int> dices = [];
void addDice() {
dices.add(dices.length);
notifyListeners();
}
void removeDice(int id) {
dices.remove(id);
print(id);
notifyListeners();
}
}
To make the example work, we need to modify the MyHomePage Column children as well, to build the list of DiceWidgets:
...dices.map((d) => DiceWidget(value: d)).toList(),
The whole example will then be:
main.dart:
class DiceNotifier extends ChangeNotifier {
List<int> dices = [];
void addDice() {
dices.add(dices.length);
notifyListeners();
}
void removeDice(int id) {
dices.remove(id);
print(id);
notifyListeners();
}
}
final diceProvider = ChangeNotifierProvider((_) {
return DiceNotifier();
});
class MyHomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final dices = watch(diceProvider).dices;
return Scaffold(
appBar: AppBar(
title: Text("Dice"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
...dices.map((d) => DiceWidget(value: d)).toList(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(diceProvider).addDice();
},
child: Icon(Icons.add),
),
);
}
}
dice_widget.dart:
class DiceWidget extends StatefulWidget {
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
class _DiceWidgetState extends State<DiceWidget> {
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
widget.value.toString() ?? '',
),
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDice(widget.value);
print(widget.value);
},
);
}
}
everyone!
I'm learning Flutter / Dart and keep having issues with instantiation and initialization.
Currently I'm writing a reorderable list but for some reason, I can't populate the list because my instance has some issue... (I think the issue is at these lines below)
List<String> tasks = [];
MyTask t;
tasks = [ t.task = 'Buy ice cream', t.task = 'Learn Flutter', t.task = 'Read books' ];)
Can you check it and give me a clue?
Thanks in advance. Any tip to seek docs is welcome!
Exception:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building _BodyBuilder:
The setter 'task=' was called on null.
Receiver: null
Tried calling: task="Buy ice cream"
Relevant code:
import 'package:flutter/material.dart';
import './bottomNavigationBar.dart';
import './ViewFeed.dart';
import './ViewNewTodo.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
//home: MyScaffold(),
initialRoute: '/',
routes: {
'/':(context) => MyFeed(),
'/toDo':(context) => MyScaffold(),
'/newToDo':(context) => MyNewTodo(),
},
);
}
}
class MyScaffold extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My ToDoS'),
),
body: MyReorderableTaskList(),
floatingActionButton: MyFloatingActionButtonNewTodo() ,
bottomNavigationBar: MyBottomNavigationBar(),
);
}
}
//Reorderable list elements
//FloatingActionButton
class MyFloatingActionButtonNewTodo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(Icons.add),
tooltip: 'Idea',
onPressed: ()=>{ Navigator.pushNamed(context, '/newToDo') },
);
}
}
class MyTask{
String task;
MyTask(this.task);
}
//ReorderableListView implementation
class MyReorderableTaskList extends StatefulWidget {
#override
_MyReorderableTaskListState createState() => _MyReorderableTaskListState();
}
class _MyReorderableTaskListState extends State<MyReorderableTaskList> {
List<String> tasks = [];
MyTask t;
void initState(){
tasks = [ t.task = 'Buy ice cream', t.task = 'Learn Flutter', t.task = 'Read books' ];
super.initState();
}
#override
Widget build(BuildContext context) {
return ReorderableListView(
onReorder: _onReorder,
children: List.generate(
tasks.length,
(index){ return MyListView(index, Key('$index'), tasks ); }
),
);
}
void _onReorder(int oldIndex, int newIndex){
setState(() {
if(newIndex > oldIndex) { newIndex -= 1; }
final String item = tasks.removeAt(oldIndex);
tasks.insert(newIndex, item);
});
}
}
class MyListView extends StatefulWidget {
final int index;
final Key key;
final List<String> listTasks;
MyListView(this.index, this.key, this.listTasks);
#override
_MyListViewState createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView> {
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(6),
child: InkWell(
splashColor: Colors.blue,
onTap: ()=>{ MaterialState.focused },
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text('Tarea: ${widget.listTasks[widget.index]} | ${widget.index}',),
),
],
),
),
);
}
}
MyTask t; is null. t isn't actually anything yet. You must create a new object MyTask() and assign it to t
Change it to
MyTask t = new MyTask("");
Edit: More thorough explanation
You can't just declare MyTask t; and then try to use t, because you have not yet said what t is. That's like declaring int a; and then trying to do print(a + 5). You can't do that, because you have not yet assigned a value to a. Similarly, MyTask t; means you have created a variable named t of type MyTask. In other words, you have declared a variable t. But you must still initialize it, in other words, assign t a value of type MyTask, otherwise the value of t will be null.
So to summarize,
MyTask t;
t.task = "Hello";
will not work, because it's like doing
int a;
int b = a + 5;
You can't add 5 to a, because although you have declared the variable a, you have not yet initialized it, so it does not yet have a value.
Likewise, you can't access the .task property of t, because you haven't said what t is. t is just null.
So you must initialize t by instantiating a new MyTask object-
MyTask t = new MyTask("");
The "" is required inside the parentheses because your MyTask class constructor requires a parameter.
class MyTask{
String task;
MyTask(this.task); // Parameter required to assign to the String task
}
This might help you.
Hello I am new to flutter and have created a form builder app. I am using Cloud Firestore as my database. The issue I am having is trying to render specific widgets based on a conditional. I have build the form and have the object data set up like this: Each projects has a list of Questions objects that hold the question information. I want to iterate through the question objects and check the question type then based on that question type render a certain widget (EX. Multiple choice, image upload, short answer, etc). Ideally it would be display as one question per page with a next button. I would like to stay on one page and just call/display other widgets/functions as the user presses next.
The issue is that I can not get the widgets to render and/or redirect by a conditional statement. Right now I set up a simple for each loop with a switch statement nested inside however since the iteration continues until it the last object the pages never render. My goal is to add a button when on pressed would continue the iteration.
I have a view project page that sets up the GetProject object and then calls the necessary class functions to get the data from firestore.
class Questions{
final String question;
final String number;
final String type;
Questions({this.question, this.number, this.type});
List<String> answers = new List();
}
class GetProject {
final String docID;
final String title;
GetProject({this.docID, this.title});
List<Questions> questions = new List();
//....Class Functions below not relevant
}
Here is what I have so far for the view project page
import 'package:flutter/material.dart';
import '../../models/project.dart';
import '../../Services/getproject.dart';
import 'textquestion.dart';
import 'multiplechoicequestion.dart';
import 'UserLocationInfo.dart';
import 'shortanswerquestion.dart';
class ViewProject extends StatefulWidget {
final String docIDref;
final String title;
ViewProject({this.docIDref, this.title});
#override
_ViewProjectState createState() => _ViewProjectState();
}
class _ViewProjectState extends State<ViewProject> {
#override
Widget build(BuildContext context) {
GetProject project = new GetProject(docID: widget.docIDref, title: widget.title);
project.getdataFromProject();
project.questions.forEach((e){
var type = e.type;
switch(type){
case 'TextInputItem':
return new TextQuestionWidget(question: e);
case 'MultipleChoice':
return new MultQuestionWidget(question: e);
case 'ShortAnswer':
return new ShortAnswerQuestion(question: e);
case 'UserLocation':
return new UserLocationInfo(question: e);
}
return null;
});
//project.printproj();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: Card(
child: ListTile(
title: Text('Print Values to debug console'),
subtitle: Text(''),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
project.printproj();
}
),
),
),
);
}
}
Here is an example of what the called widget will look like
import '../../Services/getproject.dart';
class UserLocationInfo extends StatefulWidget {
final Questions question;
UserLocationInfo({this.question});
#override
_UserLocationInfoState createState() => _UserLocationInfoState();
}
class _UserLocationInfoState extends State<UserLocationInfo> {
#override
Widget build(BuildContext context) {
return Container(
);
}
}
Very Interesting Question!!
I posted some code using FutureBuilder. When you push the button labeled 'NEXT' a Widget is displayed based in a random number (random number could be a database result)
Thank you.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Future<int> _newRandomNumber() async{
print("_newRandomNumber");
return await Random().nextInt(4);
}
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: AppBar(title: Text("Random Widget")),
body: Center(child:
FutureBuilder(
initialData: 0,
future:_newRandomNumber(),
builder: (context, snapshot) {
if(snapshot.hasData){
return getRandomWidget(snapshot.data);
}
}
)
)),
);
}
Widget getRandomWidget(int randomNumber) {
switch(randomNumber){
case 0:
return Column(children: <Widget>[
Text("TextInputItem",textScaleFactor: 4),
getNextButton()
]);
break;
case 1:
return Column(children: <Widget>[
Text("MultipleChoice",textScaleFactor: 4),
getNextButton()
]);
break;
case 2:
return Column(children: <Widget>[
Text("ShortAnswer",textScaleFactor: 4),
getNextButton()
]);
break;
case 3:
return Column(children: <Widget>[
Text("UserLocation",textScaleFactor: 4),
getNextButton()
]);
break;
}
}
Widget getNextButton(){
return RaisedButton(
child: Text("NEXT"),
color: Colors.red,
onPressed: () {
setState(() {
_newRandomNumber();
});
}
);
}
}
It seems like you have few problems that needs to be addressed in your code, so this might not be what exactly you want to achieve, but it will give you a general directions to go from here.
class ViewProject extends StatefulWidget {
final String docIDref;
final String title;
ViewProject({this.docIDref, this.title});
#override
_ViewProjectState createState() => _ViewProjectState();
}
class _ViewProjectState extends State<ViewProject> {
GetProject project;
int _currentPage = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
// when there is no questions(still loading), show a progress indicator
body: project.questions == null
? Center(child: CircularProgressIndicator())
// IndexedStack will hide all children except the "index"th child
: IndexedStack(
index: _currentPage,
children: project.questions
.map((question) => _question(question))
.toList(),
),
);
}
#override
void initState() {
project = GetProject(docID: widget.docIDref, title: widget.title);
super.initState();
}
// Call this function when you want to move to the next page
void goToNextPage() {
setState(() {
_currentPage++;
});
}
Future<void> _getQuestions() async {
// you mentioned you use firebase for database, so
// you have to wait for the data to be loaded from the network
await project.getdataFromProject();
setState(() {});
}
Widget _question(Questions question) {
switch (question.type) {
case 'TextInputItem':
return TextQuestionWidget(question: e);
case 'MultipleChoice':
return MultQuestionWidget(question: e);
case 'ShortAnswer':
return ShortAnswerQuestion(question: e);
case 'UserLocation':
return UserLocationInfo(question: e);
}
}
}
I was not sure where you wanted to call the goToNextPage(), so you would have to decide where to call it.