Создание виджетов в просмотре страницы на основе условной логики
В настоящее время возникает проблема с условной логикой и виджетом PageView. Допустим, PageView будет динамически генерировать 3 страницы. На этих трех страницах будут созданы разные виджеты. Один из виджетов - это кнопка (называемая "Далее"), которая является PageController, но этот виджет должен быть заменен виджетом кнопки, который должен отправить (называемый "Отправить") всю форму (PageView завернут в форма).
Это кажется очевидным, просто напишите условную логику, которая сравнивает текущую страницу PageView с длиной PageView (PageView заполняется списком, поэтому длину легко получить). Затем переключите виджеты, когда соблюдаются правильные условия: когда текущая страница равна 3, измените виджет. К сожалению, PageView отображает кнопку "Далее" на каждой странице. Таким образом, только когда я перейду на последнюю страницу и снова нажму "Далее", она изменится на "Отправить". Предполагается, что это будет "Отправить", когда пользователь попадет на последнюю страницу.
const int TRIVIA_STARTING_TIME = 10;
class TriviaOneForm extends StatefulWidget {
final UserRepository _userRepository;
TriviaOneForm({Key key, @required UserRepository userRepository})
: assert(userRepository != null),
_userRepository = userRepository,
super(key: key);
State<TriviaOneForm> createState() => _TriviaOneFormState();
}
class _TriviaOneFormState extends State<TriviaOneForm> {
final TextEditingController _answerController = TextEditingController();
UserRepository get _userRepository => widget._userRepository;
TriviaOneBloc _triviaOneBloc;
PageController _pageController;
Timer _timer;
bool _isLoadingScreen;
bool _isNextOrSubmitButton;
int _start;
int _indexOfCarouselItem;
List<int> _selectedValList;
List _triviaDataList;
@override
void initState() {
super.initState();
_isLoadingScreen = true;
_getTriviaData();
_pageController = PageController();
_indexOfCarouselItem = 0;
_isNextOrSubmitButton = true;
_selectedValList = [0, 0, 0, 0, 0];
_triviaDataList = [];
_start = TRIVIA_STARTING_TIME;
_triviaOneBloc = BlocProvider.of<TriviaOneBloc>(context);
_answerController.addListener(_onAnswerChanged);
}
@override
void dispose() {
if (_timer != null) {
_timer.cancel();
}
_pageController.dispose();
super.dispose();
}
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(
() {
if (_start < 1) {
timer.cancel();
} else {
_start = _start - 1;
}
},
),
);
}
@override
Widget build(BuildContext context) {
return BlocListener<TriviaOneBloc, TriviaOneState>(
listener: (context, state) {
if (state.isFailure) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Submition Failure'),
Icon(Icons.error)
],
),
backgroundColor: Colors.red,
),
);
}
if (state.isSubmitting) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Submitting Answers...'),
],
),
),
);
}
if (state.isSuccess) {
BlocProvider.of<TriviaOneBloc>(context).add(Submitted());
}
},
child: BlocBuilder<TriviaOneBloc, TriviaOneState>(
builder: (context, state) {
return _isLoadingScreen
? _displayLoadScreen()
: Padding(
padding: EdgeInsets.all(20.0),
child: Form(
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
reverse: false,
scrollDirection: Axis.horizontal,
children: _triviaDataList.map<Widget>((triviaData) {
return ListView(
shrinkWrap: true,
children: <Widget>[
Text(triviaData.getQuestion),
ListView(
shrinkWrap: true,
children: triviaData.getAnswers
.map<Widget>((triviaAnswer) {
int index =
triviaData.getAnswers.indexOf(triviaAnswer);
return ListTile(
title: Text(triviaAnswer.getAnswer),
leading: Radio(
value: index,
groupValue:
_selectedValList[_indexOfCarouselItem],
onChanged: (int value) {
setState(() {
print(value);
_selectedValList[_indexOfCarouselItem] =
value;
});
},
),
);
}).toList(),
),
_isNextOrSubmitButton ? _nextButton() : _submitButton(),
RaisedButton(
onPressed: () {
startTimer();
},
child: Text('Start'),
),
Text('$_start'),
],
);
}).toList(),
),
),
);
},
),
);
}
Widget _triviaControlButton(PageController pageController) {
if (0 < _triviaDataList.length) {
return RaisedButton(
child: Text('Next'),
onPressed: () {
pageController.nextPage(
duration: Duration(seconds: 1), curve: Curves.easeInOut);
print('Next');
},
);
} else if (pageController.page.toInt() == _triviaDataList.length) {
return RaisedButton(
child: Text('Submit'),
onPressed: () {
print('Submit');
},
);
} else {
return RaisedButton(
child: Text('Error'),
onPressed: () {
print('Error');
},
);
}
}
Widget _displayLoadScreen() {
return Container(
alignment: Alignment(0.0, 0.0),
child: CircularProgressIndicator(),
);
}
void _onAnswerChanged() {
_triviaOneBloc.add(AnswerChanged(answer: _answerController.text));
}
void _getTriviaData() async {
var data = _userRepository.retrieveTriviaData();
// Await trivia data to be retrieved from firebase
await data.getDocuments().then((collection) {
collection.documents.forEach((document) {
TriviaData triviaData = TriviaData();
List<TriviaAnswer> triviaAnswerList = List<TriviaAnswer>();
// Iterate through all of the answers for a question
// Create a list of TriviaAnswer objects to hold key and value
document.data['answers'].forEach((key, value) {
TriviaAnswer triviaAnswer = TriviaAnswer();
triviaAnswer.setAnswer = key;
triviaAnswer.setAnswerValue = value;
triviaAnswerList.add(triviaAnswer);
});
// Assign question String and answer List to TriviaData
// Add all data to data list
triviaData.setAnswers = triviaAnswerList;
triviaData.setQuestion = document.data['question'];
_triviaDataList.add(triviaData);
});
});
setState(() {
_isLoadingScreen = false;
});
}
Widget _nextButton() {
return RaisedButton(
child: Text('Next'),
onPressed: () {
if (_indexOfCarouselItem < _triviaDataList.length) {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
}
if (_indexOfCarouselItem == _triviaDataList.length) {
Future.delayed(const Duration(seconds: 0), () {
setState(() {
_isNextOrSubmitButton = false;
});
});
}
try {
if (_timer != null || !_timer.isActive) {
startTimer();
}
} catch (_) {
print('Error: Timer is already disabled');
}
},
);
}
Widget _submitButton() {
return RaisedButton(
child: Text('Submit'),
onPressed: () {
print(_selectedValList);
_userRepository.storeTriviaToFirebase();
setState(() {
if (_timer != null || _timer.isActive) {
_timer.cancel();
}
});
},
);
}
}
РЕДАКТИРОВАТЬ 1: это обновленный код, который я использую для кнопки для заполнения в PageView. Я устанавливаю для String начальное значение "Next", а затем обновляю его, когда _indexOfCarouselItem + 2 == _triviaDataList.length истинно. Обновленное значение будет "Отправить", когда условие будет выполнено.
Widget _triviaControlButton() {
return RaisedButton(
child: Text(buttonText),
onPressed: () {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
if (_indexOfCarouselItem + 2 == _triviaDataList.length) {
setState(() {
buttonText = "Submit";
});
}
if (_indexOfCarouselItem < _triviaDataList.length) {
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
}
print(_indexOfCarouselItem);
print(_triviaDataList.length);
},
);
}
1 ответ
Я сейчас говорю по телефону, поэтому не могу гарантировать, что отправленный мной код в порядке, но вы поняли идею.
Во-первых: я не думаю, что вам нужны 2 кнопки, если они равны по размеру и т.д., поэтому вы можете реализовать что-то вроде этого:
child: Text( _indexOfCarouselItem += 1 != _triviaDataList.length
? 'Next' : 'Submit')
А затем используйте ту же логику в onPressed:
onPressed() {
_indexOfCarouselItem += 1 != _triviaDataList.length ? doSomethibg : doSomethingDifferent;
}
Изменить: Хорошо, если я правильно понимаю, проблема в том, что из-за перехода на кнопке написано "Отправить", но еще нет вопросов? Если это так, как вы сказали, добавьте задержку, но я думаю, что лучше будет связать текст кнопки с вопросом. Я имею в виду, что вы можете сохранить фактическую логику (потому что она работает) и добавить что-то вроде этого:
child: Text (_indexOfCarouselItem += 1!= _triviaDataList.length && questionText!= ""? 'Далее': 'Отправить')
Эта логика также может быть применена в блоке if ... else ...
Изменить 2: попробуйте это:
Widget _triviaControlButton() {
return RaisedButton(
child: Text(buttonText),
onPressed: () {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
if (_indexOfCarouselItem < _triviaDataList.length) {
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
if (_indexOfCarouselItem == _triviaDataList.length) {
setState(() {
buttonText = "Submit";
});
}
},
);
}