In my mobile application, I am initializing a Stateful widget from another widget but I always get an exception
[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception:
LateInitializationError: Field '_customAppLoaderState#64195267' has
not been initialized
Below is the code for custom_loader.dart
import 'package:SMedoApp/util/app_textstyles.dart';
import 'package:SMedoApp/util/color_constants.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CustomAppLoader extends StatefulWidget {
// const CustomAppLoader({Key? key}) : super(key: key);
late final _CustomAppLoaderState _customAppLoaderState;
#override
State<CustomAppLoader> createState() {
_customAppLoaderState=_CustomAppLoaderState();
return _customAppLoaderState;
}
void setLoaderVisible(bool _visibility){
_customAppLoaderState.setVisibility(_visibility);
}
void setProgressPerc(double progress){
_customAppLoaderState.setProgressValue(progress: progress);
}
void setCancelToken(CancelToken cancelToken) {
_customAppLoaderState.setCancelToken(cancelToken: cancelToken);
}
}
class _CustomAppLoaderState extends State<CustomAppLoader> {
bool isLoaderVisible=false;
double _progress=0.0;
CancelToken? _cancelToken;
bool isCancelButtonVisible=false;
#override
Widget build(BuildContext context) {
return Visibility(
visible: isLoaderVisible,
child: Center(
child: Container(
color: ColorConstants.black.withOpacity(0.8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SpinKitWave(
size: 50,
color: ColorConstants.white,
type: SpinKitWaveType.start
),
SizedBox(
height: 50,
),
Container(
width: 200,
child: LinearProgressIndicator(
backgroundColor: ColorConstants.white,
valueColor: new AlwaysStoppedAnimation<Color>(ColorConstants.facebook_blue),
value: _progress,
minHeight: 2,
),
),
SizedBox(
height: 10,
),
Visibility(
visible: isCancelButtonVisible,
child: TextButton(onPressed: (){
_cancelToken?.cancel();
if(_cancelToken!.isCancelled)
Navigator.pop(context);
}, child: Text(AppLocalizations.of(context)!.cancel, style: AppTextStyle.whiteOnBlackSmallWhite(context),), ),
)
],
)),
),
);
}
void setVisibility(bool _visibility){
setState(() {
isLoaderVisible=_visibility;
});
}
void setProgressValue({required double progress}) {
setState(() {
_progress=progress;
}
);
}
void setCancelToken({required CancelToken cancelToken}) {
setState(() {
_cancelToken=cancelToken;
isCancelButtonVisible=true;
});
}
}
And this is how I invoke custom_loader from another widget
CustomAppLoader loader=CustomAppLoader();
loader.setProgressPerc(0.25);
Where am I going wrong? (I am new to flutter/ dart).
createState() is not called yet on initialization of the CustomAppLoader, so when you call setProgressPerc the state doesn't exist yet. It's also not really common to save the state in a variable and using it like that.
My IDE also actually suggest that you shouldn't do any logic in the createState():
Related
I want to animate my list back and forth with scroll animation this FoodFragment call at the persistent bottom navigation
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:ikwte/AppConstant/StaticData.dart';
import 'package:ikwte/AppConstant/ThemeColors.dart';
import 'package:ikwte/AppUtils/AppUtils.dart';
import 'package:ikwte/ProviderControllers/FoodController.dart';
import 'package:ikwte/Widgets/FragmentWidgets/FoodHelper.dart';
import 'package:ikwte/main.dart';
import 'package:provider/provider.dart';
class FoodFragment extends StatefulWidget {
const FoodFragment({Key? key}) : super(key: key);
#override
State<FoodFragment> createState() => _FoodFragmentState();
}
class _FoodFragmentState extends State<FoodFragment> {
ScrollController _scrollController = ScrollController();
var themeColor = ThemeColors();
var utils = AppUtils();
var static = Statics();
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
double minScrollExtent1 = _scrollController.position.minScrollExtent;
double maxScrollExtent1 = _scrollController.position.maxScrollExtent;
animateToMaxMin(maxScrollExtent1, minScrollExtent1, maxScrollExtent1, 1,
_scrollController);
print("Function Triggered");
});
print("InitState Triggered");
}
animateToMaxMin(double max, double min, double direction, int seconds,
ScrollController scrollController) {
scrollController
.animateTo(direction,
duration: Duration(seconds: seconds), curve: Curves.linear)
.then((value) {
direction = direction == max ? min : max;
animateToMaxMin(max, min, direction, seconds, scrollController);
});
}
#override
Widget build(BuildContext context) {
FoodHelper helper = FoodHelper(context, _scrollController);
return RefreshIndicator(
backgroundColor: themeColor.yellowColor,
color: themeColor.blueColor,
onRefresh: () async {
await apisCall();
},
child: Scaffold(
backgroundColor: themeColor.blueColor,
body: Column(
children: [
utils.statusBar(context, color: themeColor.blueColor),
Expanded(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: themeColor.blueColor,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
helper.appBar(),
helper.searchFieldText(),
helper.topListHeading(),
helper.topList(),
helper.whatAreYouLookingForHeading(),
helper.whatAreYouLookingForList(),
helper.collaborationsHeading(),
helper.collaborationList(),
helper.newInTownHeading(),
helper.newInTownList(),
helper.allRestaurantsHeading(),
helper.allRestaurantsList()
],
),
)),
),
utils.bottomBar(context, color: themeColor.blueColor),
],
),
),
);
}
apisCall() {
navigatorkey.currentContext!
.read<FoodController>()
.getAllRestaurantsListApi();
navigatorkey.currentContext!.read<FoodController>().getCategoryListApi();
navigatorkey.currentContext!
.read<FoodController>()
.getTopRestaurantsListApi();
navigatorkey.currentContext!
.read<FoodController>()
.getNewInTownRestaurantApi();
navigatorkey.currentContext!.read<FoodController>().getSecretMenuApi();
}
}
But the issue i am facing right now is AddpostFrameCallback Function is not calling on the persistent bottom navigation so my animation is not getting trigged i tried multiple Solutions but it didn't work for me .How can i reslove this issue
I have a Home Screen Widget, that plays a fullscreen background video using the video_player package.
This code works fine for me:
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
VideoPlayerController _controller;
void initState() {
super.initState();
// Pointing the video controller to mylocal asset.
_controller = VideoPlayerController.asset("assets/waterfall.mp4");
_controller.initialize().then((_) {
// Once the video has been loaded we play the video and set looping to true.
_controller.play();
_controller.setLooping(true);
// Ensure the first frame is shown after the video is initialized.
setState(() {});
});
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Stack(
children: <Widget>[
SizedBox.expand(
child: FittedBox(
// If your background video doesn't look right, try changing the BoxFit property.
// BoxFit.fill created the look I was going for.
fit: BoxFit.fill,
child: SizedBox(
width: _controller.value.size?.width ?? 0,
height: _controller.value.size?.height ?? 0,
child: VideoPlayer(_controller),
),
),
),
Container(
child: Center(
child: Text('Hello!'),
),
),
],
),
),
);
}
}
The question is, how can I implement this using flutter Hooks? I understand that I have to use useEffect() to implement the functionality of initState() and dispose(), useFuture() and maybe useMemoized() to handle asynchronous _controller.initialize() call and what possibly else? But, I cannot glue them to get the desired result. Can anyone indicate to me the "using Hooks" implementation of the above code?
I was looking for the answer to how to convert a VideoPlayer demo from StatefulWidget to HookWidget when I came across this question. I've come up with something that works so I'll post it here since there is nothing elsewhere that I could find and some others are hitting this page looking for an answer.
I used a viewmodel. The video controller is a property of the viewmodel. This code will not compile since some of the controls are not included. But it will demonstrate the structure and incorporation of the viewmodel.
Here's the widget file:
import 'package:flutter/foundation.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:video_player/video_player.dart';
import 'intro_viewmodel.dart';
class IntroPage extends HookWidget {
Future<void> saveAndGetStarted(BuildContext context) async {
final IntroViewModel introViewModel = context.read(introViewModelProvider);
await introViewModel.completeIntro();
}
Future<void> onNext(BuildContext context) async {
final IntroViewModel introViewModel = context.read(introViewModelProvider);
await introViewModel.incrementIntro();
}
final List<SliderModel> slides = [
SliderModel(
description: 'A word with you before you get started.\n',
title: 'Why This App?',
localImageSrc: 'media/Screen1-Movingforward-pana.svg',
backgroundColor: Colors.lightGray),
SliderModel(
description: 'This information will help the app be more accurate\n',
title: 'Personal Profile',
localImageSrc: 'media/Screen2-Teaching-cuate.svg',
backgroundColor: Colors.lightGray)
];
#override
Widget build(BuildContext context) {
final IntroViewModel introViewModel = context.read(introViewModelProvider);
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
children: [
Text(
slides[introViewModel.index].description,
style: Theme.of(context).textTheme.headline5,
textAlign: TextAlign.center,
),
Expanded(
child: FractionallySizedBox(
widthFactor: .98,
heightFactor: .5,
child: VideoPlayer(introViewModel.videoController),
)),
Align(
alignment: Alignment.bottomCenter,
child: CustomRaisedButton(
onPressed: () {
if (introViewModel.index == slides.length - 1) {
saveAndGetStarted(context);
} else {
onNext(context);
}
},
color: Theme.of(context).accentColor,
borderRadius: 15,
height: 50,
child: Text(
introViewModel.index == 0
? 'Continue'
: 'Save and Get Started',
style: Theme.of(context)
.textTheme
.headline5
.copyWith(color: Colors.white),
),
),
),
],
),
),
));
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IterableProperty<SliderModel>('slides', slides));
}
}
And here is the viewmodel code
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:video_player/video_player.dart';
import '../top_level_providers.dart';
final introViewModelProvider = ChangeNotifierProvider<IntroViewModel>((ref) {
//this singleton class provides global access to selected variables
final SharedPreferencesService localSharedPreferencesService =
ref.watch(sharedPreferencesService);
return IntroViewModel(localSharedPreferencesService);
});
class IntroViewModel extends ChangeNotifier {
IntroViewModel(this.localSharedPreferencesService) : super() {
state = localSharedPreferencesService?.isIntroComplete();
// Pointing the video controller to my local asset.
videoController = VideoPlayerController.asset('media/test_search.mp4');
videoController.initialize().then((_) {
// Once the video has been loaded we play the video and set looping to true.
// not autoplaying yet
// videoController.play();
// videoController.setLooping(true);
});
}
final SharedPreferencesService localSharedPreferencesService;
VideoPlayerController videoController;
bool state = false;
int index = 0;
Future<void> completeIntro() async {
await localSharedPreferencesService.setIntroComplete();
state = true;
notifyListeners();
}
Future<void> incrementIntro() async {
++index;
notifyListeners();
}
bool get isIntroComplete => state;
}
I have a problem with that situation. Can you help me ? I'm taking this error message.
Exception has occurred.
I want save a thing in a list but show:
The following NoSuchMethodError was thrown while handling a gesture:
The method 'salvar' was called on null.
Receiver: null
Tried calling: salvar(Instance of 'Tarefa')
THE CODE:
class TarefaScreen extends StatefulWidget {
#override
_TarefaScreenState createState() => _TarefaScreenState();
}
class _TarefaScreenState extends State<TarefaScreen> {
final _formKey = GlobalKey<FormState>();
TarefaService _tarefaService;
String _titulo;
String _descricao;
DateTime _dataHora;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
_save() {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
Tarefa _tarefa = Tarefa(
titulo: this._titulo,
descricao: this._descricao,
dataHora: this._dataHora);
this._tarefaService.salvar(_tarefa).then((value) {
showInfo("Tarefa adicionada");
Navigator.of(context).pop();
}
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Adicionar Tarefa")
,
),
body: Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
children: <Widget>[...
Padding(
padding: const EdgeInsets.only(top: 30, left: 10, right: 10, bottom: 5),
child: RaisedButton(
child: Text("Enviar"),
onPressed: () {
this._save();
},
),
),
],
),
),
);
}
}
And I define addItem here;
class TarefaService {
final TarefaStore tarefaStore;
TarefaService(this.tarefaStore);
Future<List<Tarefa>> buscarTarefas() {
return Future.value(tarefaStore.tarefas);
}
Future<Tarefa> salvar(Tarefa atividade){
tarefaStore.adicionarTarefa(atividade);
return Future.value(atividade);
}
void dispose(){
}
}
Please, help me
You are trying to call the method on the object which is not instantiated so its null in this._tarefaService.salvar(_tarefa). You need to instantiate _tarefaService. You can do it in init()
_tarefaService = new TarefaService(tarefaStore);
I am trying to create my own custom segment in flutter. That segment has two buttons, one for teachers and other for students. What I am trying to do, it's encapsulate the buttons in one Stateful Widget to handle the setState of both buttons, because I want the buttons to be an AnimatedContainer and if I rebuild the childrens (the buttons) from the parent the transition doesn't works.
Note that the buttons are Stack positioned and I reorder the content to get the tapped button over the other (that will has effect when I set more width in the tapped button, now this is not created yet).
Here is my code:
import 'package:flutter/cupertino.dart';
import '../../app_localizations.dart';
import '../../styles.dart';
GlobalKey<_ButtonState> teachersButtonKey = GlobalKey();
GlobalKey<_ButtonState> studentsButtonKey = GlobalKey();
String _globalTappedButtonId = 'teachersButton';
class FiltersAppBarSegment extends StatefulWidget {
#override
_FiltersAppBarSegmentState createState() => _FiltersAppBarSegmentState();
}
class _FiltersAppBarSegmentState extends State<FiltersAppBarSegment> {
List<Widget> buildStackChildren(SegmentChangedCallBack handleSegmentChanged) {
if (_globalTappedButtonId == 'teachersButton') {
return <Widget>[
Container(
key: UniqueKey(),
child: _Button(
key: studentsButtonKey,
id: 'studentsButton',
label: 'seeStudents',
rightPosition: 1,
onSegmentChanged: handleSegmentChanged,
),
),
Container(
key: UniqueKey(),
child: _Button(
key: teachersButtonKey,
id: 'teachersButton',
label: 'amTeacher',
rightPosition: null,
onSegmentChanged: handleSegmentChanged,
),
),
];
} else {
return <Widget>[
Container(
key: UniqueKey(),
child: _Button(
key: driverButtonKey,
id: 'driverButton',
label: 'amDriver',
rightPosition: null,
onSegmentChanged: handleSegmentChanged,
),
),
Container(
key: UniqueKey(),
child: _Button(
key: studentsButtonKey,
id: 'studentButton',
label: 'amStudent',
rightPosition: 1,
onSegmentChanged: handleSegmentChanged,
),
),
];
}
}
void handleSegmentChanged(String clickedButtonId) {
teachersButtonKey.currentState._handleButtonTapped();
studentsButtonKey.currentState._handleButtonTapped();
}
#override
Widget build(BuildContext context) {
return Container(
height: 42,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Stack(children: buildStackChildren(handleSegmentChanged)),
);
}
}
class _Button extends StatefulWidget {
final String id;
final String label;
final double rightPosition;
final void onSegmentChanged;
_Button({
Key key,
this.id,
this.label,
this.rightPosition,
this.onSegmentChanged,
}) : super(key: key);
#override
_ButtonState createState() => _ButtonState();
}
class _ButtonState extends State<_Button> {
bool _tapped;
double _topPosition;
double _width;
double _height;
double _getTopPosition() => _tapped ? 0 : 5;
double _getHeight() => _tapped ? 42 : 32;
Gradient _getGradient() {
if (_tapped) {
return Styles.darkAccentColorGradient;
} else {
return Styles.darkAccentColorGradientDisabled;
}
}
void _handleButtonTapped() {
setState(() {
_globalTappedButtonId = widget.id;
_tapped = (widget.id == _globalTappedButtonId);
_topPosition = _getTopPosition();
_height = _getHeight();
});
}
#override
void initState() {
super.initState();
_tapped = (widget.id == _globalTappedButtonId);
_topPosition = _getTopPosition();
_height = _getHeight();
}
#override
Widget build(BuildContext context) {
return Positioned(
top: _topPosition,
right: widget.rightPosition,
child: GestureDetector(
onTap: () {
widget.onSegmentChanged('test');
},
child: AnimatedContainer(
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
width: _width,
height: _height,
decoration: BoxDecoration(
gradient: _getGradient(),
borderRadius: BorderRadius.circular(13),
),
child: Center(
child: Text(
AppLocalizations.of(context).translate(widget.label),
style: Styles.bodyWhiteText,
textAlign: TextAlign.center,
),
),
),
),
);
}
}
I'm sure you have already found a solution to your problem by now, but this question is one of the first search results when looking at this error.
As you already know, per the Flutter doc on GlobalKey:
"You cannot simultaneously include two widgets in the tree with the
same global key. Attempting to do so will assert at runtime."
You can define your own individual keys like:
import 'package:flutter/widgets.dart';
class TestKeys{
static final testKey1 = const Key('__TESTKEY1__');
static final testKey2 = const Key('__TESTKEY2__');
...
}
And then reference them in the widget with key: TestKeys.testKey1
This was described in this question here so perhaps it can help someone with the need for a similar use case.
There are also a few solutions listed in this GitHub issue
I want to make a reusable button with a container in GestureDetector which will execute some function if I tap it and its color will become dark if I hold it. Any help, hint, tip would be very much appreciated.
I tried writing the GestureDetector in the custom widget file but it gives me errors.
When i try to extract widget on the GestureDetector it gives an Reference to an enclosing class method cannot be extracted error.
(the main page)
import 'package:flutter/material.dart';
import 'ReusableTwoLineList.dart';
import 'Text_Content.dart';
const mainTextColour = Color(0xFF212121);
const secondaryTextColour = Color(0xFF757575);
const inactiveBackgroundCardColor = Color(0xFFFFFFFF);
const activeBackgroundCardColor = Color(0xFFE5E5E5);
enum CardState {
active,
inactive,
}
class SettingsPage extends StatefulWidget {
#override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
CardState currentCardState = CardState.inactive;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings'),
),
body: ListView(
children: <Widget>[
GestureDetector(
onTapDown: (TapDownDetails details) {
setState(() {
currentCardState = CardState.active;
});
},
onTapCancel: () {
setState(() {
currentCardState = CardState.inactive;
});
},
onTap: () {
setState(() {
currentCardState = CardState.inactive;
//some random function
});
},
child: ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
backgroundCardColor: currentCardState == CardState.active
? activeBackgroundCardColor
: inactiveBackgroundCardColor,
cardChild: TextContent(
mainLabel: 'First Day',
secondaryLabel: 'This is the first day of the week',
),
),
),
ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
cardChild: TextContent(
mainLabel: '2nd day',
secondaryLabel: 'This is the end day',
),
),
ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
),
],
),
);
}
}
ReusableTwoLineList.dart (the custom widget i am trying to make)
class ReusableTwoLineList extends StatelessWidget {
ReusableTwoLineList({
#required this.mainTextColor,
#required this.secondaryTextColor,
this.backgroundCardColor,
this.cardChild,
this.onPressed,
});
final Color mainTextColor, secondaryTextColor, backgroundCardColor;
final Widget cardChild;
final Function onPressed;
#override
Widget build(BuildContext context) {
return Container(
color: backgroundCardColor,
padding: EdgeInsets.symmetric(horizontal: 16),
height: 72,
width: double.infinity,
child: cardChild,
);
}
}
This is what i want but in a custom widget so i can use it over and over.
Normal-https://i.imgur.com/lVUkMFK.png
On Pressed-https://i.imgur.com/szuD4ZN.png
You can use extract method instead of extract widget. Flutter will add everything as it is, and instead of a class you will get a reusable function.