Flutter Switch - onChanged Not Changing - dart

When using the following Switch widget, the isOn value always returns true and never changes.
The Switch only moves position on a swipe too, a tap won't move it. How to resolve?
bool isInstructionView = false;
Switch(
value: isInstructionView,
onChanged: (bool isOn) {
setState(() {
isInstructionView = isOn;
print(isInstructionView);
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
)
Update: For extra clarity, onChanged always returns isOn as true. Why would this be?

I added additional code chunks according to #Zulfiqar 's answer. I didn't test this code but I m using similar codes in my project. if you want to save it and use in another class or if you want to show latest state for everytime you load you can save the state in a global variable and call it when you load the class. hope it will help..
class Tab_b extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<Tab_b>{
bool isInstructionView;
#override
void initState() {
isInstructionView = Global.shared.isInstructionView;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text("add data"),
),
body: new Container(
child: Switch(
value: isInstructionView,
onChanged: (bool isOn) {
print(isOn);
setState(() {
isInstructionView = isOn;
Global.shared.isInstructionView = isOn;
isOn =!isOn;
print(isInstructionView);
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
);
}
}
class Global{
static final shared =Global();
bool isInstructionView = false;
}

You just need to make sure to declare the bool for the switch toggle outside Widget build to make it global and acessible for SetState method. No need to initstate and etc.
class ExampleClass extends StatefulWidget {
#override
_ExampleClassState createState() => _ExampleClassState();
}
class _ExampleClassState extends State<ExampleClass> {
bool isInstructionView = false;
#override
Widget build(BuildContext context) {
return Container(
child: Switch(
value: isInstructionView,
onChanged: (isOn) {
setState(() {
isInstructionView = isOn
});
print(isInstructionView);
},
...
),
);
}
}

class Tab_b extends StatefulWidget {
bool isInstructionView = false;
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<Tab_b>{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text("add data"),
),
body: new Container(
child: Switch(
value: widget.isInstructionView,
onChanged: (bool isOn) {
print(isOn);
setState(() {
widget.isInstructionView = isOn;
print(widget.isInstructionView);
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
);
}

Here Is my code for toggle button
class ToggleButtonScreen extends StatefulWidget {
#override
_ToggleButtonScreenState createState() => _ToggleButtonScreenState();
}
class _ToggleButtonScreenState extends State<ToggleButtonScreen> {
bool _value = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: _value ? AssetImage("images/cnw.png") : AssetImage("images/cnw.png"),
fit: BoxFit.cover,
),
),
child: Padding(
padding: EdgeInsets.all(AppDimens.EDGE_REGULAR),
child: Column(
children: [
// _customSwitchButton(),
_normalToggleButton(),
],
),
),
),
),
),
);
}
Widget _normalToggleButton () {
return Container(
child: Transform.scale(
scale: 2.0,
child: Switch(
activeColor : Colors.greenAccent,
inactiveThumbColor: Colors.redAccent,
value: _value,
activeThumbImage: AssetImage("images/cnw.png"),
inactiveThumbImage : AssetImage("images/simple_interest.png"),
onChanged: (bool value){
setState(() {
_value = value;
});
},
),
),
);
}
}

You need to rebuild the widget when the state changes. refer the documentation
https://docs.flutter.io/flutter/material/Switch/onChanged.html

I faced the same issue , the problem is your
bool isInstructionView = false;
is in same build method which will get rebuild due to change of switch to render new UI on setState()
Solution is to move it out of function Scope to Class Scope so that your variable do not change on rendering Widget again

To get Switch to work , move the setState(() {}) outside of Switch in a callback function .
// Switch Widget
Switch( value: _toggleState,
onChanged: _attemptChange,
),
//Callback
void _attemptChange(bool newState) {
setState(() {
_toggleState = newState;
newState ? _switchCase = 'ON' : _switchCase = 'OFF';
});

Change SwitchListTile instead of Switch will work.
bool isSwitched = false;
SwitchListTile(
title: Text("title"),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.symmetric(),
value: isSwitched ,
onChanged: (bool value) {
setState(() {
isSwitched = value;
});
}
)

Use Transform when use Switch will work.
bool isSwitched = false;
Transform.scale(
scale: 1.8,
child: Switch(
onChanged: (value) {
setState(() {
isSwitched = value;
});
},
value: isSwitched,
),
)

I have also got the same problem while I was implementing CupertinoSwitch. I was able to turn it ON but wasn't able to turn it OFF.
According to this example, I tried to put my Switch inside the widget called 'Semantics' and magically it started working.
Below is the code:
body: Column(
children: <Widget>[
SizedBox(height: 50.0,),
Semantics(
container: true,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
CupertinoSwitch(
value: _switchValue,
onChanged: (bool value){
setState(() {
_switchValue = value;
_animating = value;
});
},
),
Text(
"${_switchValue ? "On" : "Off"}",
style: TextStyle(fontSize: 20.0,
fontWeight: FontWeight.bold),
),
],
),
),
SizedBox(
height: 50.0,
),
Visibility(
child: CupertinoActivityIndicator(
animating: _animating,
radius: 30.0,
),
visible: _animating,
),
],
),
Hope it helps.

Related

How to prevent multiple touch on Flutter Inkwell

I new to flutter and i have a counter button that i want to prevent it from multiple touch.
The Tap Function is defined under Inkwell component (onTap: () => counterBloc.doCount(context)).
if i run this apps and doing multi touch, counter will go up quickly, but i dont want it happen. any idea ?
below are my code :
Expanded(
child: Container(
padding: EdgeInsets.only(right: 16),
alignment: Alignment.centerRight,
child: InkWell(
onTap: () => counterBloc.doCount(context),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Image.asset("assets/images/home/tap.png", scale: 11,),
StreamBuilder(
initialData: 0,
stream: counterBloc.counterStream,
builder: (BuildContext ctx, AsyncSnapshot<int> snapshot){
return Text("${snapshot.data}",style: TextStyle(color: Colors.white, fontSize: 120),);
},
),
],
)
)
)
)
you can use an AbsorbPointer
AbsorbPointer(
absorbing: !enabled,
child: InkWell(
onTap: (){
print('buttonClicked');
setState(() {
enabled = false;
});
},
child: Container(
width: 50.0,
height: 50.0,
color: Colors.red,
),
),
),
and when you want to enable the button again, set the enabled to true, don't forget to wrap it with a setState
Try this? It should solve your problem.
class SafeOnTap extends StatefulWidget {
SafeOnTap({
Key? key,
required this.child,
required this.onSafeTap,
this.intervalMs = 500,
}) : super(key: key);
final Widget child;
final GestureTapCallback onSafeTap;
final int intervalMs;
#override
_SafeOnTapState createState() => _SafeOnTapState();
}
class _SafeOnTapState extends State<SafeOnTap> {
int lastTimeClicked = 0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
final now = DateTime.now().millisecondsSinceEpoch;
if (now - lastTimeClicked < widget.intervalMs) {
return;
}
lastTimeClicked = now;
widget.onSafeTap();
},
child: widget.child,
);
}
}
You can wrap any kind of widget if you want.
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
// every click need to wait for 500ms
SafeOnTap(
onSafeTap: () => log('500ms'),
child: Container(
width: double.infinity,
height: 200,
child: Center(child: Text('500ms click me')),
),
),
// every click need to wait for 2000ms
SafeOnTap(
intervalMs: 2000,
onSafeTap: () => log('2000ms'),
child: Container(
width: double.infinity,
height: 200,
child: Center(child: Text('2000ms click me')),
),
),
],
),
),
),
);
}
}
Another option is to use debouncing to prevent this kind of behaviour ie with easy_debounce, or implementing your own debounce.
You can also use IgnorePointer
IgnorePointer(
ignoring: !isEnabled
child: yourChildWidget
)
And when you disable the component, it starts ignoring the touches within the boundary of the widget.
I personally wouldn't rely on setState, I'd go with a simple solution like this:
Widget createMultiClickPreventedButton(String text, VoidCallback clickHandler) {
var clicked = false;
return ElevatedButton(
child: Text(text),
onPressed: () {
if (!clicked) {
clicked = true;
clickHandler.call();
}
});
}
You can also use a Stream to make counter to count only on debounced taps.
final BehaviourSubject onTapStream = BehaviourSubject()
#override
void initState() {
super.initState();
// Debounce your taps here
onTapStream.debounceTime(const Duration(milliseconds: 300)).listen((_) {
// Do something on tap
print(1);
});
}

Flutter: How to show a CircularProgressIndicator before WebView loads the page?

I'm using the webview_fluttter plugin, but I can't find a way to show a CircularProgressIndicator before the webview shows the page...
What's the equivalent of Androids WebViewClient onPageStarted/onPageFinished?
WebView(
initialUrl: url,
onWebViewCreated: (controller) {
},
)
In version 0.3.5 there is 'onPageFinished' callback. You can create WebView container with IndexedStack.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class _WebViewContainerState extends State < WebViewContainer > {
var _url;
final _key = UniqueKey();
_WebViewContainerState(this._url);
num _stackToView = 1;
void _handleLoad(String value) {
setState(() {
_stackToView = 0;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: IndexedStack(
index: _stackToView,
children: [
Column(
children: < Widget > [
Expanded(
child: WebView(
key: _key,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: _url,
onPageFinished: _handleLoad,
)
),
],
),
Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(),
),
),
],
)
);
}
}
class WebViewContainer extends StatefulWidget {
final url;
WebViewContainer(this.url);
#override
createState() => _WebViewContainerState(this.url);
}
This is working properly for me
initState() {
isLoading = true;
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("Your Title",centerTitle: true
),
body: Stack(
children: <Widget>[
new WebView(
initialUrl: /* YourUrl*/,
onPageFinished: (_) {
setState(() {
isLoading = false;
});
},
),
isLoading ? Center( child: CircularProgressIndicator()) : Container(),
],
),
);
}
class _WebViewContainerState extends State<WebViewContainer> {
var _url;
final _key = UniqueKey();
bool _isLoadingPage;
Completer<WebViewController> _controller = Completer<WebViewController>();
_WebViewContainerState(this._url);
#override
void initState() {
super.initState();
_isLoadingPage = true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
new WebView(
key: _key,
initialUrl: _url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (webViewCreate) {
_controller.complete(webViewCreate);
},
onPageFinished: (finish) {
setState(() {
_isLoadingPage = false;
});
},
),
_isLoadingPage
? Container(
alignment: FractionalOffset.center,
child: CircularProgressIndicator(),
)
: Container(
color: Colors.transparent,
),
],
),
);
}
}
Optional parameters hidden and initialChild are available so that you can show something else while waiting for the page to load.If you set hidden to true it will show a default CircularProgressIndicator. If you additionally specify a Widget for initialChild you can have it display whatever you like till page-load.
check this page : flutter_webview_plugin
and you can specify what you want to show with initialChild
return new MaterialApp(
title: 'Flutter WebView Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_) => new WebviewScaffold(
url: selectedUrl,
appBar: new AppBar(
title: const Text('Widget webview'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
child: const Center(
child: CircularProgressIndicator(),
),
),
),
},
);
I use a combination of webview_flutter, progress_indicators
Here is a sample working code:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:async';
import 'package:progress_indicators/progress_indicators.dart';
class ContactUs extends StatefulWidget {
#override
_ContactUsState createState() => _ContactUsState();
}
class _ContactUsState extends State<ContactUs> {
bool vis1 = true;
Size deviceSize;
#override
Widget build(BuildContext context) {
deviceSize = MediaQuery.of(context).size;
final lindicator = Center(
child: AnimatedOpacity(
// If the Widget should be visible, animate to 1.0 (fully visible). If
// the Widget should be hidden, animate to 0.0 (invisible).
opacity: vis1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box needs to be the child of the AnimatedOpacity
child: HeartbeatProgressIndicator(
child: Container(
width: 100.0,
height: 50.0,
padding: EdgeInsets.fromLTRB(35.0,0.0,5.0,0.0),
child: Row(
children: <Widget>[
Icon(
Icons.all_inclusive, color: Colors.white, size: 14.0,),
Text(
"Loading View", style: TextStyle(color: Colors.white, fontSize: 6.0),),
],
),
),
),
),
);
return new Scaffold(
appBar: new AppBar(
title: new Row(
children:<Widget>[
Text('THisApp'),
lindicator,
]),
backgroundColor: Colors.red,
),
body: new Container(
child:WebView(
initialUrl: 'https://cspydo.com.ng/',
javaScriptMode: JavaScriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController){
setState(() {
vis1=false;
});
},
)
),
);
}
}
Found the solution.You can add initialChild and set attribute hidden as true.
WebviewScaffold(
hidden: true,
url:url,initialChild: Center(
child: Text("Plase Wait...",style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.deepPurpleAccent[100]
),)
) )
You can work on isLoading and change it after you are sure data is loaded properly.
class X extends StatefulWidget {
XState createState() => XState();
}
class XState extends State<X>{
bool isLoading = false;
#override
void initState() {
setState(() {
isLoading = true;
});
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
isLoading ? Center(child: CircularProgressIndicator()) : WebView(...)
]
)
);
}
}
I used A stack in flutter.
#override
void initState() {
isLoading = true;
super.initState();
}
in the build method
Stack(
children: [
isLoading ? Loading() : Container(),
Container(
child: Flex(
direction: Axis.vertical,
children: [
Expanded(
child: InAppWebView(
contextMenu: contextMenu,
initialUrl: url,
onLoadSop: (InAppWebViewController controller, url) {
setState(() {
isLoading = false;
});
},
)
),
],
),
),
],
)

Drop down button in flutter not switching values to the selected value

I've recently started programming using dart and flutter and everything has been going smoothly for my app, although recently i wanted to add drop down menu to provide the user with multiple options to pick from. everything worked as planned however when i pick a value from the list it doesn't change the value in the box, it goes back to the hint or an empty box. any help would be appreciated!
here is my code for the dropdownbutton:
Widget buildDropdownButton() {
String newValue;
return new Padding(
padding: const EdgeInsets.all(24.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new ListTile(
title: const Text('Frosting'),
trailing: new DropdownButton<String>(
hint: Text('Choose'),
onChanged: (String changedValue) {
newValue=changedValue;
setState(() {
newValue;
print(newValue);
});
},
value: newValue,
items: <String>['None', 'Chocolate', 'Vanilla', 'ButterCream']
.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()),
),
],
),
);
}
The error is because you are declaring a method variable newValue you must declare that variable as global inside your StatefulWidget.
String newValue;
Widget buildDropdownButton() {
return new Padding(
padding: const EdgeInsets.all(24.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new ListTile(
title: const Text('Frosting'),
trailing: new DropdownButton<String>(
hint: Text('Choose'),
onChanged: (String changedValue) {
newValue=changedValue;
setState(() {
newValue;
print(newValue);
});
},
value: newValue,
items: <String>['None', 'Chocolate', 'Vanilla', 'ButterCream']
.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()),
),
],
),
);
}
Faced same issue and none of the answers worked. Then, I found the solution in one of my old projects.
I was using it in a AlertDialog here.
So, Change DropdownButton to DropdownButtonFormField
and add onSaved exactly as onChanged:
onSaved: (value) {
setState(() {
_selectedValue = value;
});
}
That's it. It will start working.
I had this problem although I was already using the solution above.
for anyone who has this problem and the above solution does not work, try separating FutureBuilder from the dropdown. this is how your final code should look like:
class TheFuture extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: myFuture(),
builder: (ctx, snp) {
if (!snp.hasData) return LoadingLine();
return TheBuilder(snp.data);
},
);
}
}
class TheBuilder extends StatefulWidget {
const TheBuilder(this.mp);
final Map<String, dynamic> mp;
#override
_MessageUSScreenFilterBodyState createState() =>
_MessageUSScreenFilterBodyState();
}
class _MessageUSScreenFilterBodyState extends State<MessageUSScreenFilterBody> {
int _selectedId;
#override
Widget build(BuildContext context) {
return DropdownButton<int>(
selectedItemBuilder: (context) =>
widget.mp['myData'].map((e) => Text(e.type)).toList(),
items: widget.mp['myData']
.map(
(e) => DropdownMenuItem(
child: Text(e.type),
value: e.id,
),
)
.toList(),
value: _selectedId,
onChanged: (int _id) {
setState(() {
_selectedId = _id;
});
},
);
}
}
wrap dropdown button with StatefulBuilder and initialise newValue outside build method.
StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: Text("Change Status"),
content: Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1),
borderRadius: BorderRadius.circular(5)),
child: DropdownButtonHideUnderline(
child: DropdownButton(
hint: Text('Choose'),
onChanged: (String changedValue) {
setState(() {
newValue = changedValue;
print(newValue);
});
},
value: newValue,
items: <String>[
'None',
'Chocolate',
'Vanilla',
'ButterCream'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()),
),
),
),
));

flutter checkbox doesn't work

I want to build a checkbox with CheckboxListTile inside this widget dialog but when I tap the checkbox the checked on the checkbox doesn't change.
This is my code:
Future<Null> _showGroupDialog(BuildContext context) async {
await showDialog(
context: context,
builder: (BuildContext dialogContext) =>
Dialog(child: _buildCheckboxGroups(context)));
}
Widget _buildCheckboxGroups(BuildContext context) {
List<Widget> childrens = List.generate(_groups.length, (index) {
return CheckboxListTile(
title: Text(_groups[index].name),
value: _groups[index].checked,
onChanged: (bool val) {
setState(() {
_groups[index].checked = val;
});
},
);
});
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: childrens,
));
}
Btw, the onChange method is invoked when I tap the checkbox. Can anyone solve this?
class _MyHomePageState extends State<MyHomePage>{
//<Here you have to set default value for _groups[index].checked to false/true>
#override
Widget _buildCheckboxGroups(BuildContext context) {
List<Widget> childrens = List.generate(_groups.length, (index) {
return CheckboxListTile(
title: Text(_groups[index].name),
value: _groups[index].checked,
onChanged: (bool val) {
setState(() {
_groups[index].checked = val;
});
},
);
});}
Working Example is given below
class _MyHomePageState extends State<MyHomePage> {
bool flagWarranty=false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: new Container(
padding: new EdgeInsets.all(20.0),
child: new Form(
key: this._formKey,
child: new ListView(
children: <Widget>[
new Checkbox(
value: flagWarranty,
onChanged: (bool value) {
setState((
) {
flagWarranty=value;
});
},
)
],),)));
}}

My CachedNetworkImage doesn't change when value changed

#override
Widget build(BuildContext context) {
var switchButton = new Switch(
value: detail,
onChanged: (bool value){
setState(() {
detail = value;
});
},
);
var imagejadwal = new CachedNetworkImage(
imageUrl: switchButton.value?"https://drive.google.com/uc?export=download&id=1v-dA5bG7Fwk_hJvL2wu4Z9P10JdsaWIe":"https://drive.google.com/uc?export=download&id=1qfdI_yM7rzdLMqRizlr76445qc0IQKhD",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
);
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Column(
children: <Widget>[
new Align(
alignment: Alignment.topRight,
child: switchButton,
),
imagejadwal,
],
)
);
}
It's because the CachedNetworkImage or my code is wrong ? Can someone
help me ? I'm still new at flutter Thank you.
Lib: https://github.com/renefloor/flutter_cached_network_image
I tried the code of aziza, but it also didn't work for me.
I changed a bit of code in the CachedNetworkImage and that seems to work, I changed the 'didUpdateWidget':
#override
void didUpdateWidget(CachedNetworkImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.imageUrl != oldWidget.imageUrl ||
widget.placeholder != widget.placeholder){
_imageProvider = new CachedNetworkImageProvider(widget.imageUrl,
errorListener: _imageLoadingFailed);
_resolveImage();
}
}
It needs to change its ImageProvider. I made an issue for that on github
You could also use a Stack. In that way you have more control over the animation from one image to the other. For example
class _CachedImageExampleState extends State<CachedImageExample> {
bool switchState = true;
#override
Widget build(BuildContext context) {
var switchButton = new Switch(
value: switchState,
onChanged: (bool value) {
setState(() {
switchState = value;
});
},
);
var imagejadwal1 = new CachedNetworkImage(
imageUrl: "https://drive.google.com/uc?export=download&id=1v-dA5bG7Fwk_hJvL2wu4Z9P10JdsaWIe",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
);
var imagejadwal2 = new CachedNetworkImage(
imageUrl: "https://drive.google.com/uc?export=download&id=1qfdI_yM7rzdLMqRizlr76445qc0IQKhD",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
);
return new Scaffold(
appBar: new AppBar(
title: new Text("TestImage"),
),
body: new Column(
children: <Widget>[
new Align(
alignment: Alignment.topRight,
child: switchButton,
),
new Container (
//width: 500.0,
///container to deal with the overflow, you may not want to use it with hardcoded
///height because it will not allow the view to be responsive, but it is just to
///make a point about dealing with the overflow
height: 400.0,
child: new Stack(children: <Widget>[
new Opacity(opacity: switchState ? 1.0 : 0.0, child: imagejadwal1),
new Opacity(opacity: switchState ? 0.0 : 1.0, child: imagejadwal2,)
],)),
],
)
);
}
}
I noticed that the animation of the switch is not shown when the second image (the blue one) is being shown. It is a very large image (2550x3300), consider making that one smaller to improve the performance of the app.
Your code is working fine, however you have got two issue to deal with:
The images are overflowing and may be preventing the UI from updating.
The images are too big, and you need to wait a little bit for them to load.
I have modified a little bit of your code:
class _CachedImageExampleState extends State<CachedImageExample> {
bool switchState = true;
#override
Widget build(BuildContext context) {
var switchButton = new Switch(
value: switchState,
onChanged: (bool value) {
setState(() {
switchState = value;
});
},
);
var imagejadwal = new CachedNetworkImage(
imageUrl: switchState
? "https://drive.google.com/uc?export=download&id=1v-dA5bG7Fwk_hJvL2wu4Z9P10JdsaWIe"
: "https://drive.google.com/uc?export=download&id=1qfdI_yM7rzdLMqRizlr76445qc0IQKhD",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
);
return new Scaffold(
appBar: new AppBar(
title: new Text("TestImage"),
),
body: new Column(
children: <Widget>[
new Align(
alignment: Alignment.topRight,
child: switchButton,
),
new Container (
//width: 500.0,
///container to deal with the overflow, you may not want to use it with hardcoded
///height because it will not allow the view to be responsive, but it is just to
///make a point about dealing with the overflow
height: 400.0,
child: imagejadwal),
],
)
);
}
}
I managed to fix your issue, there are two implementations for this plugin and the following one should fix it for you. I am not sure the reason behind state not updating (probably imageUrl can not be overriden)
Anyway, here is your fix:
class CachedImageExample extends StatefulWidget {
#override
_CachedImageExampleState createState() => new _CachedImageExampleState();
}
class _CachedImageExampleState extends State<CachedImageExample> {
bool toggle = true;
#override
Widget build(BuildContext context) {
var switchButton = new Switch(
value: toggle,
onChanged: (bool value) {
setState(() {
toggle = value;
});
},
);
var img= new Image(image: new CachedNetworkImageProvider(
toggle
? "https://drive.google.com/uc?export=download&id=1v-dA5bG7Fwk_hJvL2wu4Z9P10JdsaWIe"
: "https://drive.google.com/uc?export=download&id=1qfdI_yM7rzdLMqRizlr76445qc0IQKhD"));
return new Scaffold(
appBar: new AppBar(
title: new Text("TestImage"),
),
body: new Column(
children: <Widget>[
new Align(
alignment: Alignment.topRight,
child: switchButton,
),
new Container (
width: 200.0,
height: 200.0,
child: img),
],
)
);
}
}
Update : Fit the image to the whole screen
class CachedImageExample extends StatefulWidget {
#override
_CachedImageExampleState createState() => new _CachedImageExampleState();
}
class _CachedImageExampleState extends State<CachedImageExample> {
bool toggle = true;
#override
Widget build(BuildContext context) {
var switchButton = new Switch(
activeColor: Colors.amber,
activeTrackColor: Colors.amberAccent,
inactiveThumbColor: Colors.amber,
value: toggle,
onChanged: (bool value) {
setState(() {
toggle = value;
});
},
);
return new Scaffold(
appBar: new AppBar(
actions: <Widget>[
switchButton
],
title: new Text("TestImage"),
),
body:
new Container (
decoration: new BoxDecoration(
image: new DecorationImage(
fit: BoxFit.cover,
image:
new CachedNetworkImageProvider(
toggle
? "https://drive.google.com/uc?export=download&id=1v-dA5bG7Fwk_hJvL2wu4Z9P10JdsaWIe"
: "https://drive.google.com/uc?export=download&id=1qfdI_yM7rzdLMqRizlr76445qc0IQKhD"
)
),
),
),
);
}
}

Resources