Flutter How to make an image not show on a condition - flutter

I need an image to show or not show, on a condition and the condition is that if the answer of the user is correct than the button will show but if the answer of the question is wrong than the image will not display. the condition is if (joinedWords !=
data[widget.CurrentIndex].sentence // dont display the next
level image. if the
asnwer is correct than the button will show.
String joinedWords =
selectedWords.join(" ").toString();
String _setRatingOnAlert() {
//print(controller.duration?.inSeconds);
print(data![widget.CurrentIndex].timer);
print(data[widget.CurrentIndex].level);
print(joinedWords);
print(data[widget.CurrentIndex].sentence);
print(widget.CurrentIndex);
if (joinedWords ==
data[widget.CurrentIndex].sentence) {
starsRating = 'assets/threestars_small.png';
} else {
starsRating = 'assets/zerostars_small.png';
}
return starsRating;
}
return Container(
child: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding:
const EdgeInsets.only(right: 110),
child: FlatButton(
onPressed: () {
setState(() {
selectedWords.clear();
Navigator.pop(context);
});
},
child: Image.asset(
'assets/restartbutton.small.png',
height: 80,
width: 80,
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding:
const EdgeInsets.only(left: 110),
child: FlatButton(
onPressed: () {
Navigator.pop(context);
/// E close munu
Navigator.pop(context);
/// E hek current quiz page
Navigator.push(
/// E qel next level quiz
context,
MaterialPageRoute(
builder: (context) =>
RenditFjaletButton(
QuizList: widget
.AllQuizLists[
widget.CurrentIndex +
1]
.word,
AllQuizLists:
widget.AllQuizLists,
CurrentIndex:
widget.CurrentIndex + 1,
),
),
);
},
child: Image.asset(
'assets/next_small.png', // this is the button that i
dont want to show if the
answer is wrong
height: 80,
width: 80,
),
),
),
),

use ternary operator in this case.
bool answer = false;
//do some operation with answer;
(answer)? ShowImage():Container();
Widget showImage(){
return Container(
child: Image.network(
'imagePath',
fit: BoxFit.fitWidth,
);
}

You can use ternary operator incase you need the conditions.Do as follows:
isTrue? Image.asset():Container()

Related

How to add button on top of image in flutter?

I'm trying to load image from network and display it fully along with button on top of the image. To achieve this I looked up on various solution and found that this can be done using Stack widget. My implementation is as below
class DisplayImage extends StatefulWidget {
final String text;
DisplayImage({required this.text}) ;
#override
State<DisplayImage> createState() => _DisplayImageState();
}
class _DisplayImageState extends State<DisplayImage> {
#override
initState() {
// TODO: implement initState
_asyncMethod();
super.initState();
}
_asyncMethod() async {
Image.network(widget.text);
setState(() {
dataLoaded = true;
});
}
bool dataLoaded = false;
#override
Widget build(BuildContext context) {
if (dataLoaded){
return Scaffold(
backgroundColor: Colors.lightBlueAccent,
appBar: AppBar(title: Text("Selfie BGchanger"),centerTitle: true,
),
body: Center(child: Stack(
children: [Image.network(
widget.text,
fit: BoxFit.fill,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
),
const SizedBox(height: 50,),
Align(
alignment: Alignment(0, .2),
child: ElevatedButton(child: const Text('Save',style: TextStyle(fontWeight: FontWeight.normal)),style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
primary: Colors.black,
// padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
textStyle: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold)),
onPressed: () async{
String url = widget.text;
var imageId = await ImageDownloader.downloadImage(url);
if(imageId == null)
{return;}
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Saved to gallery!')));
Fluttertoast.showToast(msg: "Image saved to Gallery");
},
),
),
],),
),
);
} else {
return CircularProgressIndicator(backgroundColor: Colors.cyan,strokeWidth: 5,);
}
}
}
with this I get image is as below
save button is on top but what I'm trying to get is as below
Expected:
full sized image with save button on bottom center
I tried using boxfit.cover with height and width as infinit as below
fit: BoxFit.cover,
// height: double.infinity,
// width: double.infinity,
I got display error
How can I fix this to get expected image ? any help or suggestion on this will be highly appreciated
update:
based on answer suggestion I modified code as above and get output as below
Wrap your ElevatedButton widget with Positioned/Align widget.
Align(
alignment: Alignment(0, .2), //adjust based on your need
child: ElevatedButton(
Also you find more about Stack , Align widget.
body: Stack(
children: <Widget>[
Positioned.fill(
child: Image.network(
"",
fit: BoxFit.cover,
)),
Align(
alignment: Alignment(0, .2), // change .2 based on your need
child: ElevatedButton(
onPressed: () async {
await showDatePicker(
context: context,
initialEntryMode: DatePickerEntryMode.inputOnly,
initialDate: DateTime.now(),
firstDate: DateTime.now().subtract(Duration(days: 33)),
lastDate: DateTime.now().add(Duration(days: 33)),
);
},
child: Text("Dialog"),
),
),
],
),

Flutter: Return each iteration of loop as new page

I have a for loop which returns a class, in the class a widget is selected. I have the problem that it is returning each widget below each other, whereas I would like each widget to be its own 'page' and when the user navigates back to the 'page' with the loop on it, a new iteration of the loop is started.
Current problematic output
Game() - iteration
body: Container(
child: Column(
children: [
for (roundsindex = 0; roundsindex < rounds; roundsindex++) ...{
Cards(),
}
],
),
),
Cards() - choose widget
return Container(
width: double.infinity,
child: Column(
children: [
if (currentcat == "starter") ...[
StarterCard()
] else if (currentcat == "rather") ...[
RatherCard()
] else ...[
SpinBottleCard()
],
],
),
);
Starter() - example of widget that should take full page
return InkWell(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return Game();
},
),
),
child: Container(
width: double.infinity,
color: Colors.blueAccent,
child: Text("I am Starter"),
),
);

Set a left padding of a class tapping a button from another class

I tried using global variables to reach this:
mainly I have a web app with a sidebar that appear and disappear pressing a button.
I'm trying to use the global variable globals.sidebarWidth. Than the main layout left padding should be the globals.sidebarWidth , that take the value when I press a sidebar button that make the sidebar disappear
This is how it works:
import 'globals.dart' as globals;
....
body: Stack(
children: [
// if ((screenSize.width > 800) /*&& (menuVisibility)*/)
Container(
child: SizedBox(
height: screenSize.height,
width: screenSize.width,
child: Image.asset('sfondo-home1.jpg', fit: BoxFit.cover),
),
),
if (screenSize.width > 800) SideBar(),
Padding(
padding: EdgeInsets.only(
top: 50, left: globals.sidebarWidth.toDouble()),
child: SizedBox(
height: 100,
child: Navigator(
key: locator<NavigationService>().navigatorKey,
onGenerateRoute: generateRoute,
initialRoute: HomeRoute,
),
),
),
],
),
Then I want that if I press the arrow button in my SideBar class, all the content float to left "following" the sidebar that is retiring to left and the left padding of my page.
This is from my SideBar class:
Padding(
padding: menuVisib
? EdgeInsets.only(left: 180, top: 400)
: EdgeInsets.only(left: 30, top: 400),
child: Container(
child: CircleAvatar(
backgroundColor: Colors.blueGrey.shade800,
child: InkWell(
onTap: () {
if (menuVisib) {
setState(() {
globals.sidebarWidth = 0;
menuVisib = false;
});
} else {
setState(() {
menuVisib = true;
globals.sidebarWidth = 200;
});
setState(() {});
}
},
child:
Icon(menuVisib ? Icons.arrow_back : Icons.arrow_forward)),
),
),
But it doesn't work. How should I do? Hope I explained well.
Many thanks for your help.

Flutter overflowed positioned Button is not clickable

I have a stack widget parenting a Positioned widget like this:
Stack(
overflow: Overflow.visible,
children: [
Container(
width: 150,
height: 150,
),
Positioned(
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('FAB tapped!');
},
backgroundColor: Colors.blueGrey,
),
right: 0,
left: 0,
bottom: -26,
),
],
),
That part of the fab which is placed outside the container is not clickable, what is the solution?
and here is a screenshot:
try this :
Stack(
overflow: Overflow.visible,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>
[
Container(width: 150, height: 150, color: Colors.yellow),
Container(width: 150, height: 28, color: Colors.transparent),
],
),
Positioned(
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('FAB tapped!');
},
backgroundColor: Colors.blueGrey,
),
right: 0,
left: 0,
bottom: 0,
),
],
)
you should keep button inside of stack if you want it to stay clickable
Providing an updated answer since overflow specification is deprecated after v1.22.0-12.0.pre. clipBehavior is the replacing property:
Stack(
clipBehavior: Clip.none,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>
[
Container(width: 150, height: 150, color: Colors.yellow),
Container(width: 150, height: 28, color: Colors.transparent),
],
),
Positioned(
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('FAB tapped!');
},
backgroundColor: Colors.blueGrey,
),
right: 0,
left: 0,
bottom: 0,
),
],
)
Note: credits to #Amir's answer
The problem is when a child overflows on Stack that has Clip.none behavior, the part that is outside of Stack would not be recognized to be clicked.
Solution :
Wrap the Stack with Column and add the space you want to be outside of Stack :
final _clipSpace = 30;
Stack(
clipBehavior: Clip.none,
children: [
Column(
children: [
DecoratedBox(
decoration: const BoxDecoration(// decorate the box //
),
child: Column(
children: [
// column's children
],
)
],
),
),
// clip space
const SizedBox(height: _clipSpace,)
],
),
const Positioned(
child: _ActionButton(),
left: 0,
right: 0,
bottom: 0,
),
],
);
Container(
width: 150,
height: 180,
child: Stack(
children: [
Container(
width: double.infinity,
height: 150,
child: Image.asset('assets/images/image.jpg', fit: BoxFit.cover,)
),
Container(
alignment: Alignment.bottomCenter,
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('FAB tapped!');
},
backgroundColor: Colors.blueGrey,
),
),
],
),
),
Fab button is not clickable because it renders outside of stack as you have given -ve bottom, Ideally, you should have parent container and inside it has all stack widget you should render it.
Here I have used hardcoded values, but you should use media query as per your requirement
Like:
Container(
width: MediaQuery.of(context).size.width * 0.3,
height: MediaQuery.of(context).size.height * 0.3,
child: Stack(
children: [
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.26,
child: Image.asset('assets/images/jitesh.jpg', fit: BoxFit.cover,)
),
Container(
alignment: Alignment.bottomCenter,
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('FAB tapped!');
},
backgroundColor: Colors.blueGrey,
),
),
],
),
),
up until now, there is now solution from Flutter, I should make a simple trick to solve this issue, I need to make a layout like this
the workaround is by adding a SizedBox below your background widget, the height of the SizedBox should be the same as the height of the overlaping widget.
like this
Stack(
clipBehavior: Clip.none,
children: [
Column( // wrap the background in a column
children: [
const _HeaderBackground(),
SizedBox(height: 100), // add the SizedBox with height = 100.0
],
),
Positioned(
bottom: 16,
left: 4,
right: 4,
child: _ReferralCodeSection(customer), // the height of this widget is 100
),
],
),
You have to put the button in the last place of the Stack's children
Stack(children: [...., buttonWidget ])
Flutter does not officially plan to solve this problem, so we can only use some hacking methods.
Here is my resolution with an example, you can use the following OverflowWithHitTest Widget directlly:
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
/// Creates a widget that can check its' overflow children's hitTest
///
/// [overflowKeys] is must, and there should be used on overflow widget's outermost widget those' sizes cover the overflow child, because it will [hitTest] its' children, but not [hitTest] its' parents. And i cannot found a way to check RenderBox's parent in flutter.
///
/// The [OverflowWithHitTest]'s size must contains the overflow widgets, so you can use it as outer as possible.
///
/// This will not reduce rendering performance, because it only overcheck the given widgets marked by [overflowKeys].
///
/// Demo:
///
/// class _MyPageStore extends State<MyPage> {
///
/// var overflowKeys = <GlobalKey>[GlobalKey()];
///
/// Widget build(BuildContext context) {
/// return Scaffold(
/// body: OverflowWithHitTest(
///
/// overflowKeys: overflowKeys,
///
/// child: Container(
/// height: 50,
/// child: UnconstrainedBox(
/// child: Container(
/// width: 200,
/// height: 50,
/// color: Colors.red,
/// child: OverflowBox(
/// alignment: Alignment.topLeft,
/// minWidth: 100,
/// maxWidth: 200,
/// minHeight: 100,
/// maxHeight: 200,
/// child: GestureDetector(
/// key: overflowKeys[0],
/// behavior: HitTestBehavior.translucent,
/// onTap: () {
/// print('==== onTap;');
/// },
/// child: Container(
/// color: Colors.blue,
/// height: 200,
/// child: Text('aaaa'),
/// ),
/// ),
/// ),
/// ),
/// ),
/// ),
/// ),
/// );
/// }
/// }
///
///
class OverflowWithHitTest extends SingleChildRenderObjectWidget {
const OverflowWithHitTest({
required this.overflowKeys,
Widget? child,
Key? key,
}) : super(key: key, child: child);
final List<GlobalKey> overflowKeys;
#override
_OverflowWithHitTestBox createRenderObject(BuildContext context) {
return _OverflowWithHitTestBox(overflowKeys: overflowKeys);
}
#override
void updateRenderObject(
BuildContext context, _OverflowWithHitTestBox renderObject) {
renderObject.overflowKeys = overflowKeys;
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
DiagnosticsProperty<List<GlobalKey>>('overflowKeys', overflowKeys));
}
}
class _OverflowWithHitTestBox extends RenderProxyBoxWithHitTestBehavior {
_OverflowWithHitTestBox({required List<GlobalKey> overflowKeys})
: _overflowKeys = overflowKeys,
super(behavior: HitTestBehavior.translucent);
/// Global keys of overflow children
List<GlobalKey> get overflowKeys => _overflowKeys;
List<GlobalKey> _overflowKeys;
set overflowKeys(List<GlobalKey> value) {
var changed = false;
if (value.length != _overflowKeys.length) {
changed = true;
} else {
for (var ind = 0; ind < value.length; ind++) {
if (value[ind] != _overflowKeys[ind]) {
changed = true;
}
}
}
if (!changed) {
return;
}
_overflowKeys = value;
markNeedsPaint();
}
#override
bool hitTest(BoxHitTestResult result, {required Offset position}) {
if (hitTestOverflowChildren(result, position: position)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
bool hitTarget = false;
if (size.contains(position)) {
hitTarget =
hitTestChildren(result, position: position) || hitTestSelf(position);
if (hitTarget || behavior == HitTestBehavior.translucent)
result.add(BoxHitTestEntry(this, position));
}
return hitTarget;
}
bool hitTestOverflowChildren(BoxHitTestResult result,
{required Offset position}) {
if (overflowKeys.length == 0) {
return false;
}
var hitGlobalPosition = this.localToGlobal(position);
for (var child in overflowKeys) {
if (child.currentContext == null) {
continue;
}
var renderObj = child.currentContext!.findRenderObject();
if (renderObj == null || renderObj is! RenderBox) {
continue;
}
var localPosition = renderObj.globalToLocal(hitGlobalPosition);
if (renderObj.hitTest(result, position: localPosition)) {
return true;
}
}
return false;
}
}

List builder : how to make the first element different?

I am building a list (dynamically, throught the result of an HTTP request to an API) but I want to add a 'add' card first, before the others elements.
Like this :
I'm building this with a builder
But the way I do it is not good, there's a problem.
Here, account.trips is a list of Trip object and AddNewTrip is the 'add' card.
Because of my if, the first element of the list is always not displayed
So, is there another way to put my 'add' card as the first element of the list, without hiding one element ?
return Builder(
builder: (BuildContext context) {
if (account.trips.indexOf(i) == 0) {
return new AddNewTrip(account);
}
return Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 40.0, left: 5.0, right: 5.0),
child: Stack(
...
EDIT : here is the full code :
Expanded(
child: ScopedModelDescendant<Account>(
builder: (context, child, _account){
print(_account.profile);
return new CarouselSlider(
enableInfiniteScroll: false,
enlargeCenterPage: true,
height: MediaQuery.of(context).size.height,
initialPage: 1,
items: itemBuilder(_account)
);
},
),
)
List<Widget> itemBuilder(Account account) {
if (account.trips != null)
return account.trips.map((i) {
return Builder(
builder: (BuildContext context) {
if (account.trips.indexOf(i) == 0) {
return new AddNewTrip(account);
}
return Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 40.0, left: 5.0, right: 5.0),
child: Stack(
children: <Widget>[
Hero(
tag: i.id,
child: Material(
type: MaterialType.transparency,
child: InkWell(
child: Container(
height: 200,
margin: EdgeInsets.symmetric(horizontal: 5.0),
decoration: cardImage(i.imageUrl),
),
borderRadius: BorderRadius.circular(20.0),
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return TripDetails(i);
}));
},
),
),
),
Positioned(
width: MediaQuery.of(context).size.width * 0.7,
height: MediaQuery.of(context).size.height * 0.48,
top: MediaQuery.of(context).size.height * 0.2,
left: MediaQuery.of(context).size.width * 0.035,
child: Hero(
tag: i.id + 'container', //should be an ID
child: Material(
type: MaterialType.transparency,
child: Container(
decoration: cardDecoration,
child: new TripContent(i, true),
),
),
),
)
],
),
);
},
);
}).toList();
else
return [
new AddNewTrip(account)
];
}
The previous answers already provide a good general advice on how to do this.
For your specific example you would have to do something like this:
When looping over your accounts, prepend the result with your "Add" card like this:
List<Widget> itemBuilder(Account account) {
return [
new AddNewTrip(account),
...(account.trips?.map((i) {/** same as before without your index check **/}) ?? []),
];
}
return account.trips.map((i) {
change to:
return [[null], account.trips].expand((x) => x).toList().map((i) {
how about this you build the card first then build rest of your list..
List<Widget> itemBuilder(Account account) {
return [
AddNewTrip(account),
if (account.trips != null)
return account.trips.map((i) {
return Builder(
builder: (BuildContext context) {
return Padding(
//rest of your code
);
});
);
}];}
You can add a dummy list item in your list at 0th index and later you can perform your logic.
Below is sample code can help you out:
var list = ["1","3","4"];
list.insert(0,"5");
Output:5 1 3 4