I don't believe I am asking such a simple question.
I try to change opacity in initState() but it does not have an effect.
How do I trigger setState if I don't use a button?
Here is the DARTPAD.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: LogoFade(),
);
}
}
class LogoFade extends StatefulWidget {
#override
createState() => LogoFadeState();
}
class LogoFadeState extends State<LogoFade> {
double opacityLevel = 0.0;
void changeOpacity() {
setState(() => opacityLevel = 1.0);
}
#override
void initState() {
changeOpacity();
super.initState();
}
#override
Widget build(BuildContext context) {
// changeOpacity();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedOpacity(
opacity: opacityLevel,
duration: Duration(seconds: 6),
child: FlutterLogo(
size: 200,
),
),
],
);
}
}
I just want a simple FadeIn effect like Animate.css library. Like we make in web with the example below.
.fade-in-image {
animation: fadeIn 6s;
}
#keyframes fadeIn {
0% {opacity:0;}
100% {opacity:1;}
}
<div class="fade-in-image">
<img src="https://www.innodeed.com/wp-content/uploads/2017/11/flutter-logo.png">
</div>
In flutter app life cycle intiState() called before the Widget build(BuildContext context) called.So technically your opacity varriable always initialize with 1.You can use FutureDelay to see the effect of this animation.
void changeOpacity() async{
await Future.delayed(const Duration(milliseconds: 2000));
setState(() => opacityLevel = 1.0);
}
#override
void initState() {
changeOpacity();
super.initState();
}
Related
I'm trying to animate a property of a CustomPainter widget that get's its values from an item provided by a Riverpod Notifier.
In this broken down example of my real app, I trigger the Notifier by changing the data of the second Item which then should resize the circle in front of the ListTile.
It seems to work for changes where the value increases but when the value decreases, it often jumps over parts of the animation.
I'm not sure if I'm doing the whole animation part right here.
The code is also on Dartpad:
https://dartpad.dev/?id=e3916b47603988efabd7a08712b98287
// ignore_for_file: avoid_print
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod + animated CustomPainter',
home: const Example3(),
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.orange,
brightness: MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness,
surface: Colors.deepOrange[600],
),
),
);
}
}
class ItemPainter extends CustomPainter {
final double value;
ItemPainter(this.value);
final itemPaint = Paint()..color = Colors.orange;
#override
void paint(Canvas canvas, Size size) {
// draw a circle with a size depending on the value
double radius = size.width / 10 * value / 2;
canvas.drawCircle(
Offset(
size.width / 2,
size.height / 2,
),
radius,
itemPaint,
);
}
#override
bool shouldRepaint(covariant ItemPainter oldDelegate) => oldDelegate.value != value;
}
CustomPaint itemIcon(double value) {
return CustomPaint(
painter: ItemPainter(value),
size: const Size(40, 40),
);
}
#immutable
class Item {
const Item({required this.id, required this.value});
final String id;
final double value;
}
// notifier that provides a list of items
class ItemsNotifier extends Notifier<List<Item>> {
#override
List<Item> build() {
return [
const Item(id: 'A', value: 1.0),
const Item(id: 'B', value: 5.0),
const Item(id: 'C', value: 10.0),
];
}
void randomize(String id) {
// replace the state with a new list of items where the value is randomized from 0.0 to 10.0
state = [
for (final item in state)
if (item.id == id) Item(id: item.id, value: Random().nextInt(100).toDouble() / 10.0) else item,
];
}
}
class AnimatedItem extends StatefulWidget {
final Item item;
const AnimatedItem(this.item, {super.key});
#override
State<AnimatedItem> createState() => _AnimatedItemState();
}
class _AnimatedItemState extends State<AnimatedItem> with SingleTickerProviderStateMixin {
late final AnimationController _animationController;
late Animation<double> animation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
value: widget.item.value,
vsync: this,
duration: const Duration(milliseconds: 3000),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
void didUpdateWidget(AnimatedItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.item.value != widget.item.value) {
print('didUpdateWidget: ${oldWidget.item.value} -> ${widget.item.value}');
_animationController.value = oldWidget.item.value / 10;
_animationController.animateTo(widget.item.value / 10);
}
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return itemIcon((widget.item.value * _animationController.value));
},
);
}
}
final itemsProvider = NotifierProvider<ItemsNotifier, List<Item>>(() => ItemsNotifier());
class Example3 extends ConsumerWidget {
const Example3({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
final items = ref.watch(itemsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Animated CustomPainter Problem'),
),
// iterate over the item list in ItemsNotifier
body: ListView.separated(
separatorBuilder: (context, index) => const Divider(),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items.elementAt(index);
return ListTile(
key: Key(item.id),
leading: AnimatedItem(item),
title: Text('${item.value}'),
);
},
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () {
ref.read(itemsProvider.notifier).randomize('B'); // randomize the value of the second item
},
child: const Icon(Icons.change_circle),
),
],
),
);
}
}
Your issues lie completely in your implementation of the AnimationController. I don't really understand your intent with the original code, but the reason it jumped was because your were doing widget.item.value * _animationController.value in the build function. When you updated your item's value, it suddenly changed widget.item.value, creating the jump, then animating a small change with the AnimationController.
This code will work:
class _AnimatedItemState extends State<AnimatedItem> with SingleTickerProviderStateMixin {
late final AnimationController _animationController;
late Animation<double> animation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
value: widget.item.value,
vsync: this,
duration: const Duration(milliseconds: 3000),
lowerBound: 0.0,
upperBound: 10.0
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
void didUpdateWidget(AnimatedItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.item.value != widget.item.value) {
print('didUpdateWidget: ${oldWidget.item.value} -> ${widget.item.value}');
_animationController.animateTo(widget.item.value);
}
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return itemIcon(_animationController.value);
},
);
}
}
This code adjusts the bounds of your AnimationController to accommodate the range of values you want to animate and only uses _animationController.value in build. I also removed a redundant line from didUpdateWidget, but that had no effect on functionality.
I have a splash screen in my homepage activity which should then redirect to my second activity:
class _MyHomePageState extends State<MyHomePage> {
#override
void initState(){
super.initState();
Timer(const Duration(seconds: 3),
()=>Navigator.pushReplacement(context,
MaterialPageRoute(builder:
(context) =>
SecondScreen()
)
)
);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child:FlutterLogo(size:MediaQuery.of(context).size.height)
);
}
}
class SecondScreen extends StatelessWidget { //checking if internet connection exists here
late StreamSubscription subscription;
var isDeviceConnected = false;
bool isAlertSet = false;
#override
void initState(){
getConnectivity();
super.initState(); //initState() is undefined
}
getConnectivity() =>
subscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) async {
isDeviceConnected = await InternetConnectionChecker().hasConnection;
if (!isDeviceConnected && isAlertSet == false) {
showDialogBox();
setState(() => isAlertSet = true); //setState() is undefined
}
},
);
#override
void dispose() {
subscription.cancel();
super.dispose(); //dispose() is undefined
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment:MainAxisAlignment.center,
children:[
Image(
image: const AssetImage('images/logo.png'),
height: AppBar().preferredSize.height,),
const SizedBox(
width: 15,
),
Text(
widget.title
),
]
)
)
);
}
showDialogBox() => showCupertinoDialog<String>(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: const Text('No internet connection'),
content: const Text('Please make sure you have an active internet connection to continue'),
actions: <Widget>[
TextButton(
onPressed: () async {
Navigator.pop(context, 'Cancel');
setState(() => isAlertSet = false);
isDeviceConnected =
await InternetConnectionChecker().hasConnection;
if (!isDeviceConnected && isAlertSet == false) {
showDialogBox();
setState(() => isAlertSet = true);
}
},
child: const Text('OK'),
),
],
),
);
}
The flow is such that, in the homepage activity a splash screen will open and then it will redirect to the second activity which will check if the user has an active internet connection.
I tried changing the SecondScreen to statefulWidget, but I still keep getting the same error.
Stateless: A stateless widget is like a constant. It is immutable. If you want to change what is displayed by a stateless widget, you'll have to create a new one.
Stateful: Stateful widgets are the opposite. They are alive and can interact with the user. Stateful widgets have access to a method named setState, which basically says to the framework "Hello, I want to display something else. Can you redraw me please ?".
A stateless widget can only be drawn once when the Widget is loaded/built and cannot be redrawn based on any events or user actions.
This kind of widget has no state, so they can’t change according to an internal state, they only react to higher widget changes.
more information read this documentation StatefulWidget and StatelessWidget
convert in stateful widget
class SecondScreen extends StatefulWidget {
const SecondScreen({Key? key}) : super(key: key);
#override
State<SecondScreen> createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
there is no initState in a stateless widget but you can call a function after rebuild of a stateless widget using this:
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
// do something
print("Build Completed");
});
return Container(
color: Colors.blue,
child: WhatEverWidget()
);
}
}
Part of my Flutter app has a filter function which filters through a list of items. This being part of a UX should indicate the current operation to the user.
The issue I am facing is displaying the current item being filtered (evaluated) - the images simply won't be displayed or will "get stuck" on 1 - reason most likely being the run loop set the state too fast for the redraw to take place.
Here is a rough example of what I am trying to accomplish
import 'dart:async';
import 'package:flutter/material.dart';
void main() async {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int i = 0;
List<String> _images = [
"assets/sample/sample1.jpg",
"assets/sample/sample2.jpg",
"assets/sample/sample3.jpg",
"assets/sample/sample4.jpg",
];
#override
void initState() {
super.initState();
Timer(Duration(), () {
while (true) {
i++;
setState(() {
i = i % 4;
});
}
});
}
#override
Widget build(BuildContext context) {
Widget content = Container(
child: ClipRRect(
clipBehavior: Clip.antiAlias,
child: CircleAvatar(
radius: 24,
backgroundColor: Colors.transparent,
child: Image.asset(_images[i], gaplessPlayback: true,),
),
));
return MaterialApp(
home: Scaffold(
body: content,
),
);
}
Question:
To simplify things, how can I display a few images rapidly (and repeatedly) i.e. 1, 2, 3, 4, 1, 2, 3... with a few milliseconds delay between (at most)?
Don't use while loop because it will block the thread and does not let other tasks be executed
you could use something like Timer.periodic and it gives you the option to control the frame rate that images should change
and also don't use setState because it causes the entire widget to rebuilt you can use ValueNotifer to notify the specific widget direct with changes
and also remember to cancel the timer when your widget gets disposed
here is example
class _PageAState extends State<PageA> {
ValueNotifier<String> currentImage = ValueNotifier("0.png");
int counter=0;
Timer imageTimer;
#override
void initState() {
super.initState();
this.imageTimer=Timer.periodic(Duration(milliseconds: 100), (timer) {
counter++;
currentImage.value="${counter%3}.png";
});
}
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<String>(
valueListenable: currentImage,
builder: (context, value, child) {
return Image.asset(
value,
width: 30,
height: 30,
color: Colors.red,
);
},
);
}
#override
void dispose() {
super.dispose();
if(imageTimer!=null)imageTimer.cancel();
}
}
FLutter:
How to display video in video_player from the location of path_provider ?
you can copy paste run full code below
In demo, I use getApplicationDocumentsDirectory. you can print full path to check
make sure you have a file located in
/data/user/0/your_proejct_name/app_flutter/Movies/2019-11-08.mp4
code snippet
Future<String> load_path_video() async {
loading = true;
final Directory extDir = await getApplicationDocumentsDirectory();
setState(() {
dirPath = '${extDir.path}/Movies/2019-11-08.mp4';
print(dirPath);
loading = false;
// if I print ($dirPath) I have /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4
});
}
Container(
padding: const EdgeInsets.all(20),
child: loading
? CircularProgressIndicator()
: NetworkPlayerLifeCycle(
'$dirPath', // with the String dirPath I have error but if I use the same path but write like this /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4 it's ok ... why ?
(BuildContext context, VideoPlayerController controller) =>
AspectRatioVideo(controller)),
),
working demo
full code
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:video_player/video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String dirPath;
bool loading = false;
Future<String> load_path_video() async {
loading = true;
final Directory extDir = await getApplicationDocumentsDirectory();
setState(() {
dirPath = '${extDir.path}/Movies/2019-11-08.mp4';
print(dirPath);
loading = false;
// if I print ($dirPath) I have /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4
});
}
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
void initState() {
// TODO: implement initState
load_path_video();
super.initState();
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
body: ListView(
children: <Widget>[
Container(
padding: const EdgeInsets.all(20),
child: loading
? CircularProgressIndicator()
: NetworkPlayerLifeCycle(
'$dirPath', // with the String dirPath I have error but if I use the same path but write like this /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4 it's ok ... why ?
(BuildContext context, VideoPlayerController controller) =>
AspectRatioVideo(controller)),
),
],
),
);
}
}
class VideoPlayPause extends StatefulWidget {
VideoPlayPause(this.controller);
final VideoPlayerController controller;
#override
State createState() {
return _VideoPlayPauseState();
}
}
class _VideoPlayPauseState extends State<VideoPlayPause> {
_VideoPlayPauseState() {
listener = () {
setState(() {});
};
}
FadeAnimation imageFadeAnim =
FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0));
VoidCallback listener;
VideoPlayerController get controller => widget.controller;
#override
void initState() {
super.initState();
controller.addListener(listener);
controller.setVolume(1.0);
controller.play();
}
#override
void deactivate() {
controller.setVolume(0.0);
controller.removeListener(listener);
super.deactivate();
}
#override
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[
GestureDetector(
child: VideoPlayer(controller),
onTap: () {
if (!controller.value.initialized) {
return;
}
if (controller.value.isPlaying) {
imageFadeAnim =
FadeAnimation(child: const Icon(Icons.pause, size: 100.0));
controller.pause();
} else {
imageFadeAnim =
FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0));
controller.play();
}
},
),
Align(
alignment: Alignment.bottomCenter,
child: VideoProgressIndicator(
controller,
allowScrubbing: true,
),
),
Center(child: imageFadeAnim),
Center(
child: controller.value.isBuffering
? const CircularProgressIndicator()
: null),
];
return Stack(
fit: StackFit.passthrough,
children: children,
);
}
}
class FadeAnimation extends StatefulWidget {
FadeAnimation(
{this.child, this.duration = const Duration(milliseconds: 500)});
final Widget child;
final Duration duration;
#override
_FadeAnimationState createState() => _FadeAnimationState();
}
class _FadeAnimationState extends State<FadeAnimation>
with SingleTickerProviderStateMixin {
AnimationController animationController;
#override
void initState() {
super.initState();
animationController =
AnimationController(duration: widget.duration, vsync: this);
animationController.addListener(() {
if (mounted) {
setState(() {});
}
});
animationController.forward(from: 0.0);
}
#override
void deactivate() {
animationController.stop();
super.deactivate();
}
#override
void didUpdateWidget(FadeAnimation oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.child != widget.child) {
animationController.forward(from: 0.0);
}
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return animationController.isAnimating
? Opacity(
opacity: 1.0 - animationController.value,
child: widget.child,
)
: Container();
}
}
typedef Widget VideoWidgetBuilder(
BuildContext context, VideoPlayerController controller);
abstract class PlayerLifeCycle extends StatefulWidget {
PlayerLifeCycle(this.dataSource, this.childBuilder);
final VideoWidgetBuilder childBuilder;
final String dataSource;
}
/// A widget connecting its life cycle to a [VideoPlayerController] using
/// a data source from the network.
class NetworkPlayerLifeCycle extends PlayerLifeCycle {
NetworkPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder)
: super(dataSource, childBuilder);
#override
_NetworkPlayerLifeCycleState createState() => _NetworkPlayerLifeCycleState();
}
/// A widget connecting its life cycle to a [VideoPlayerController] using
/// an asset as data source
class AssetPlayerLifeCycle extends PlayerLifeCycle {
AssetPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder)
: super(dataSource, childBuilder);
#override
_AssetPlayerLifeCycleState createState() => _AssetPlayerLifeCycleState();
}
abstract class _PlayerLifeCycleState extends State<PlayerLifeCycle> {
VideoPlayerController controller;
#override
/// Subclasses should implement [createVideoPlayerController], which is used
/// by this method.
void initState() {
super.initState();
controller = createVideoPlayerController();
controller.addListener(() {
if (controller.value.hasError) {
print(controller.value.errorDescription);
}
});
controller.initialize();
controller.setLooping(true);
controller.play();
}
#override
void deactivate() {
super.deactivate();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.childBuilder(context, controller);
}
VideoPlayerController createVideoPlayerController();
}
class _NetworkPlayerLifeCycleState extends _PlayerLifeCycleState {
#override
VideoPlayerController createVideoPlayerController() {
return VideoPlayerController.network(widget.dataSource);
}
}
class _AssetPlayerLifeCycleState extends _PlayerLifeCycleState {
#override
VideoPlayerController createVideoPlayerController() {
return VideoPlayerController.asset(widget.dataSource);
}
}
/// A filler card to show the video in a list of scrolling contents.
Widget buildCard(String title) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: const Icon(Icons.airline_seat_flat_angled),
title: Text(title),
),
// TODO(jackson): Remove when deprecation is on stable branch
// ignore: deprecated_member_use
ButtonTheme.bar(
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('BUY TICKETS'),
onPressed: () {
/* ... */
},
),
FlatButton(
child: const Text('SELL TICKETS'),
onPressed: () {
/* ... */
},
),
],
),
),
],
),
);
}
class VideoInListOfCards extends StatelessWidget {
VideoInListOfCards(this.controller);
final VideoPlayerController controller;
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
buildCard("Item a"),
buildCard("Item b"),
buildCard("Item c"),
buildCard("Item d"),
buildCard("Item e"),
buildCard("Item f"),
buildCard("Item g"),
Card(
child: Column(children: <Widget>[
Column(
children: <Widget>[
const ListTile(
leading: Icon(Icons.cake),
title: Text("Video video"),
),
Stack(
alignment: FractionalOffset.bottomRight +
const FractionalOffset(-0.1, -0.1),
children: <Widget>[
AspectRatioVideo(controller),
Image.asset('assets/flutter-mark-square-64.png'),
]),
],
),
])),
buildCard("Item h"),
buildCard("Item i"),
buildCard("Item j"),
buildCard("Item k"),
buildCard("Item l"),
],
);
}
}
class AspectRatioVideo extends StatefulWidget {
AspectRatioVideo(this.controller);
final VideoPlayerController controller;
#override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController get controller => widget.controller;
bool initialized = false;
VoidCallback listener;
#override
void initState() {
super.initState();
listener = () {
if (!mounted) {
return;
}
if (initialized != controller.value.initialized) {
initialized = controller.value.initialized;
setState(() {});
}
};
controller.addListener(listener);
}
#override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: VideoPlayPause(controller),
),
);
} else {
return Container();
}
}
}
I'm trying to make a splash screen with stacks. When everything is done loading, i want to change the bool splashScreenIsLoading to false.
This is my code in my main.dart
bool splashScreenIsLoading = true;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Stack(
children: <Widget>[
loginScreen(),
showRightSplash(),
],
),
);
}
}
class showRightSplash extends StatefulWidget {
#override
_showRightSplashState createState() => _showRightSplashState();
}
class _showRightSplashState extends State<showRightSplash> {
#override
Widget build(BuildContext context) {
return splashScreenIsLoading == true ? splashScreen() : splashScreen2();
}
}
splashScreen() {
return Text(
"SHOW SPLASH SCREEN",
style: TextStyle(fontSize: 50, color: Colors.red),
);
}
splashScreen2() {
return Text(
"HIDE SPLASH SCREEN",
style: TextStyle(fontSize: 50, color: Colors.red),
);
}
In my file (a different .dart file) loginScreen(), I have this in initState
void initState() {
super.initState();
pageController = PageController(initialPage: 0);
doTasks()
setState(() {
splashScreenIsLoading = false;
});
}
It doesn't work, only when I hot reload when everything is loaded. Is there a solution for this?
Since your doTasks() is asynchronous function, edit your initState like this:
void initState() {
super.initState();
pageController = PageController(initialPage: 0);
doTasks().then((_){
setState(() {
splashScreenIsLoading = false;
});
});
}
Also you can simplify your code like this:
splashScreenIsLoading ? splashScreen() : splashScreen2()