If I am having an image in my HTML, I am not able to open that image to full screen, if tapped.
Below is the built in function available in flutter_html if image is tapped.
onImageTap: (url, context, attributes, element) => {
// code here
}
Is there any way we can achieve this?
I have tried below solution but it didn't worked
onImageTap: (url, context, attributes, element) => {
Scaffold(
body: GestureDetector(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Hero(
tag: 'imageHero',
child: Image.network(image),
),
),
onTap: () {
Navigator.pop(context);
},
),
)
}
You have to push it as a new page using Navigator.push
onImageTap: (url, context, attributes, element) => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FullScreenImageViewer(url)),
);
}
Here is your stateless widget:
class FullScreenImageViewer extends StatelessWidget {
const FullScreenImageViewer(this.url,{Key? key}) : super(key: key);
final String url;
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Hero(
tag: 'imageHero',
child: Image.network(url),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
It seems to need Navigator.push() or showDialog.
onImageTap is not rebuild your screen.
Related
I'm struggling hard with Flutter's sizing constraints.
I'm trying to make a custom scaffold like screen with a custom appbar and a stack below it with the lowest layer being a listview, and then maybe floating buttons on top if that.
I can't figure out what the proper solution would be to make the listview not throw "vertical viewport was given unbounded height" error.
Here's my code:
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:my/model.dart';
import 'package:my/widgets/menucard.dart';
import 'package:my/screens/my1.dart';
import 'package:my/screens/my2.dart';
import 'package:my/screens/my3.dart';
import 'package:my/provider.dart';
class myAppBar extends StatelessWidget {
myAppBar(
{super.key,
required double height,
required String title,
bool autoImplyBack = true})
: _height = height,
_title = title,
_autoback = autoImplyBack;
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: _height,
color: Colors.pink,
child: SizedBox(
width: double.infinity,
height: _height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: _height,
height: _height,
child: Navigator.canPop(context) && _autoback
? IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back),
)
: null,
),
Center(
child: Text(_title),
),
SizedBox(
width: _height,
height: _height,
child: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.close),
),
),
],
),
),
);
}
double _height;
String _title;
bool _autoback;
}
class myMenu extends StatelessWidget {
const myMenu({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
myAppBar(height: 100, title: 'Menu'),
Container(
color: Colors.teal,
child: Expanded(
child: //Stack(
//children: [
ListView(
children: [
MenuCard(
icondata: Icons.circle,
subMenu: 'my1',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const my1()),
);
}),
MenuCard(
icondata: Icons.circle,
subMenu: 'my2',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const my2()),
);
}),
MenuCard(
icondata: Icons.circle,
subMenu: 'my3',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const my3()),
);
}),
MenuCard(
icondata: Icons.circle,
subMenu: 'log out',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const my1()),
);
}),
],
),
//],
),
),
//),
],
),
);
}
}
I've tried setting the listview's shrinkwrap property to true, but that turned out to be ugly as well as not so much performant according to other threads regarding this topic.
I've also tried wrapping the listview in Expanded and Flexible, but to no avail.
You can shift the Expanded widget on top of Container.
body: Column(
children: [
myAppBar(height: 100, title: 'Menu'),
Expanded(
child: Container(
Also while you are trying to create custom AppBar, you can implements PreferredSizeWidget
class myAppBar extends StatelessWidget implements PreferredSizeWidget {
.... //get height using constructor.
#override
Widget build(BuildContext context) {
return Container(height:);
}
#override
Size get preferredSize => Size.fromHeight(height);
}
I have a drawer where the user can add items to it which are links to a different page. It works, but if I add an item and then close the drawer, when I open the drawer again that item is gone (other than the default one I have in a list). I have the code below
class SkillDrawer extends StatefulWidget {
#override
State<SkillDrawer> createState() => SkillDrawerState();
}
class SkillDrawerState extends State<SkillDrawer> {
List<Skill> _skills = [
Skill(title: "Test Title 1", pointCap: 500, id: DateTime.now().toString())
];
void _addNewSkill(String sklTitle, double sklPoints) {
final newSkill = Skill(
title: sklTitle,
pointCap: sklPoints,
id: DateTime.now().toString(),
);
setState(() {
_skills.add(newSkill);
});
}
void _startAddNewSkill(BuildContext ctx) {
showModalBottomSheet(
context: ctx,
builder: (bCtx) {
return NewSkill(_addNewSkill);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Subjects'),
),
// ## the container listing added items to drawer
Container(
height: double.maxFinite,
child: ListView.builder(
itemBuilder: (ctx, index) {
return ListTile(
title: Text(_skills[index].title),
onTap: () =>
Navigator.of(ctx).pushNamed('/skills', arguments: {
'id': _skills[index].id,
'title': _skills[index].title,
}));
},
itemCount: _skills.length,
))
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (() => _startAddNewSkill(context)),
),
);
}
}
I want to make it so that the items are not cleared when the drawer is closed, until the app is closed of course.
I have a top menu in my app, typical like on an online store (long image which is a logo of my shop on the left side and humburger menu on the right side).
I am struggling with making the logo redirecting to home page.
Is that option even possible?
I was trying many things but I get only errors. I am new in all of that and I would appreciate some help.
This is my appBar code which is a separate dart file as I didn't want to duplicate this code in every Scaffold:
import 'package:flutter/material.dart';
final appBar = AppBar(
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 35.0),
child: GestureDetector(
onTap: () {},
child: Icon(Icons.menu),
)),
],
backgroundColor: Colors.black,
title: Image.asset(
'images/logo.png',
fit: BoxFit.scaleDown,
height: 30,
width: 200,
),
);
Try to wrap your image with a gesture detector then do navigation inside its ontap callback. compare with code below ... ( NB* do some more reading on navigation https://docs.flutter.dev/cookbook/navigation/navigation-basics )
appBar: AppBar(
title: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
},
child: Image.asset(
'images/logo.png',
fit: BoxFit.scaleDown,
height: 30,
width: 200,
),
),
),
I have another solution just as collo54 shared wrap your image directly in a "GestureDetector" but I would suggest for that kind of Appbar creating a new file "my_app_bar.dart" and creating a statelessWidget that you will just need to call each time you want to show your CustomAppbar widget, like so:
class MyAppBar extends StatelessWidget {
const MyAppBar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return AppBar(
title: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
},
child: Image.asset(
'images/logo.png',
fit: BoxFit.scaleDown,
height: 30,
width: 200,
),
),
);
}
}
And then in your HomePage or in whatever page you will just need to call it like so:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: Container(),
);
}
Hope I was able to help you with your question, If you can't access your context you can allways add a global const with a navigatorKey and the call it with the currentContext, makes things real easy!
Good luck!
I have a custom popup built, and an image is supposed to change whenever one of my variables is changed. When I call the setState method, the content in my showDialog doesn't change.
What am I doing wrong, or is there a better approach? Trying to change the state so the image can be changed in the showDialog.
Here's my code:
class LocationManagerPage extends StatefulWidget {
const LocationManagerPage({Key? key}) : super(key: key);
#override
State<LocationManagerPage> createState() => _LocationManagerPageState();
}
class _LocationManagerPageState extends State<LocationManagerPage> {
String downloadURL = "";
Future _uploadFile(String path) async {
// Logic that gets the download url to an image...
// When the download url is found, calling setState method
setState(() {
downloadURL = fileUrl;
});
}
showLocationPopup() {
return showDialog(
context: context,
builder: (context) {
return Center(
child: Material(
child: Container(
width: 427,
height: 676,
decoration: BoxDecoration(...),
child: SingleChildScrollView(
child: Column(
children: [
// Popup UI Widgets,
Center(
child: Container(
height: 150,
width: 150,
decoration: BoxDecoration(),
child: ClipRRect(
child: Image.network(
image,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(20),
),
),
),
SizedBox(
height: 15,
),
Center(
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () async {
String? imageUrl = await urlFromWebImage();
print(imageUrl);
setState(() {
downloadURL = imageUrl!;
});
},
child: Button(
name: imageName,
),
),
),
),
// The rest of the popup UI
],
),
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
.... // Not Important
);
}
}
To update dialog ui you need to use StatefulBuilder widget on showDialog's builder and use StatefulBuilder's setState.
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
you can use the following approach
first initialize download url like this
ValueNotifier<String> downloadUrl = ValueNotifier("");
ValueListenableBuilder(
valueListenable: downloadUrl,
builder: (context, value, Widget? c) {
return Container(
height: 150,
width: 150,
decoration: BoxDecoration(),
child: ClipRRect(
child: Image.network(
downloadUrl.value, // here put download url it will auto update
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(20),
));
});
and without using setstate put value in download url it will auto update ui
downloadUrl.value = "" //your image url
or you can use StateFulBuilder
setstate rebuild your whole widget but upper approach only build image widget
I have a requirement where I need to scroll through a list of images and also zoom and pan them. Very similar to a pdf document viewer. So I Used a ListView to show the pages and added the ListView as child to InteractiveViewer.
After zooming in I could not scroll to the top or bottom end of the ListView.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: InteractiveViewer(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: 10,
itemBuilder: (context, _index) {
print(_index);
return Container(
color: Colors.grey,
height: MediaQuery.of(context).size.width * 1.1,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(2),
child: Container(
padding: EdgeInsets.all(2),
color: Colors.white,
child: Center(
child: Text('Page index: $_index'),
),
),
);
},
),
scaleEnabled: true,
panEnabled: true,
),
);
}
I guess it might be due to the InteractiveViewer handling the scroll gesture of ListView.
Is there a way to avoid vertical gesture to be handled by InteractiveViewer?
I don't think there is a way to make the two element have their scroll behavior working together, as InteractiveViewer allows you to move after zooming in your image.
Would it fulfill your requirement to set the image to fullscreen when taping on the image to zoom ?
That way you keep the scroll handled by the ScrollView and separate the InteractiveViewer to another view.
Something like that, you wrap all of your images with the ImageDetails widget and remove your InteractiveViewer from the Scaffold:
class ImageDetails extends StatelessWidget {
final String url;
const ImageDetails({Key key, this.url}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Hero(
tag: 'tag$url',
child: NetworkImage(
imageUrl: url,
fit: BoxFit.cover,
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return FullScreenImage(
url: url,
);
}));
});
}
}
class FullScreenImage extends StatelessWidget {
final String url;
const FullScreenImage({Key key, this.url})
: super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
child: InteractiveViewer(
maxScale: 2.0,
minScale: 1.0,
child: Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Hero(
tag: 'tag$url',
child: SizedBox(
width: MediaQuery.of(context).size.width,
child:
NetworkImage(imageUrl: url, fit: BoxFit.fitWidth),
),
),
),
),
),
onTap: () {
Navigator.pop(context);
},
);
}
}