New to flutter. Working on a personal project. Stuck with a small issue related to show images. Here is my widget code which I'm using for showing images.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cached_network_image/cached_network_image.dart';
class UserProfile extends StatefulWidget {
#override
UserProfileState createState() => new UserProfileState();
}
class UserProfileState extends State<UserProfile> {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Map userDetails = {};
String profileImgPath;
#override
void initState() {
super.initState();
getUserDetails();
}
Future<Null> getUserDetails() async {
try {
final SharedPreferences prefs = await _prefs;
this.userDetails = json.decode(prefs.getString('user'));
if (prefs.getString('user') != null) {
if (this.userDetails['isLoggedIn']) {
setState(() {
this.profileImgPath = this.userDetails['profileImg'];
print('Shared preference userDetailsss : ${this.userDetails}');
});
}
} else {
print('Shared preference has no data');
}
} catch (e) {
print('Exception caught at getUserDetails method');
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
Widget profileImage = new Container(
margin: const EdgeInsets.only(top: 20.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
children: <Widget>[
new CircleAvatar(
backgroundImage: (this.profileImgPath == null) ? new AssetImage('images/user-avatar.png') : new CachedNetworkImageProvider(this.profileImgPath),
radius:50.0,
)
],
)
)
],
)
);
return new Scaffold(
appBar: new AppBar(title: new Text("Profile"), backgroundColor: const Color(0xFF009688)),
body: new ListView(
children: <Widget>[
profileImage,
],
),
);
}
}
What I'm trying to do is, show the default user-avatar.png image as long as CachedNetworkImageProvider don't get original image. But, it's bit behaving differently.
Whenever I'm opening the page - I'm getting a blank blue box then suddenly the original image from CachedNetworkImageProvider comes up.
Can't able to understand what's happening.
#Jonah Williams for your reference -
CachedNetworkImage can't be used for backgroundImage property because it does not extends ImageProvider. You can create a custom CircleAvatar like described below to use the CachedNetworkImage package:
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class CustomCircleAvatar extends StatelessWidget {
final int animationDuration;
final double radius;
final String imagePath;
const CustomCircleAvatar({
Key key,
this.animationDuration,
this.radius,
this.imagePath
}) : super(key: key);
#override
Widget build(BuildContext context) {
return new AnimatedContainer(
duration: new Duration(
milliseconds: animationDuration,
),
constraints: new BoxConstraints(
minHeight: radius,
maxHeight: radius,
minWidth: radius,
maxWidth: radius,
),
child: new ClipOval(
child: new CachedNetworkImage(
errorWidget: (context, url, error) => Icon(Icons.error),
fit: BoxFit.cover,
imageUrl: imagePath,
placeholder: (context, url) => CircularProgressIndicator(),
),
),
);
}
}
And how to use:
body: new Center(
child: new CustomCircleAvatar(
animationDuration: 300,
radius: 100.0,
imagePath: 'https://avatars-01.gitter.im/g/u/mi6friend4all_twitter?s=128',
),
),
Maybe it is not the better way. But, it works!
(I'm assuming that CachedNetworkImageProvider is actually CachedNetworkImage from this package).
This line of code will always display the second image.
(this.profileImgPath == null)
? new AssetImage('images/user-avatar.png')
: new CachedNetworkImageProvider(this.profileImgPath)
Since profileImagePath is not null, the AssetImage is never created. Even if it was, as soon as it's not the cached network image will replace it before it has loaded. Instead, use the placeholder parameter of the network image. This will display your asset image until the network image loads.
new CachedNetworkImage(
placeholder: new AssetImage('images/user-avatar.png'),
imageUrl: profileImgPath,
)
Related
I am working on a app where user can logged into a app and can enter a text in a textformfileld it and can also apply different fontstyle and font family and generate a image from it.
is it possible ?
Any help would be highly appreciated
Here is an example for you. You can test it in an app.
It draw the text to a canvas then save canvas as an image. So you can apply any font style to ParagraphBuilder.
I hope it helps
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController controller = TextEditingController();
Image? img;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(
controller: controller,
),
ElevatedButton(onPressed: _onPressedButton, child: const Text("Create Image")),
Container(width: 200, height: 200, color: Colors.blueGrey, child: img ?? Container())
],
),
),
);
}
void getCanvasImage(String str) async {
var builder = ParagraphBuilder(ParagraphStyle(fontStyle: FontStyle.normal));
builder.addText(str);
Paragraph paragraph = builder.build();
paragraph.layout(const ParagraphConstraints(width: 100));
final recorder = PictureRecorder();
var newCanvas = Canvas(recorder);
newCanvas.drawParagraph(paragraph, Offset.zero);
final picture = recorder.endRecording();
var res = await picture.toImage(100, 100);
ByteData? data = await res.toByteData(format: ImageByteFormat.png);
if (data != null) {
img = Image.memory(Uint8List.view(data.buffer));
}
setState(() {});
}
void _onPressedButton() {
getCanvasImage(controller.text);
}
}
You need to send your text through External API like this https://rapidapi.com/seikan/api/img4me-text-to-image-service/ after that you need to download the image and show it to the user
I'm trying to save a picture of widget which contains a image and a few texts with it.
I tried the screenshot package and it is working perfectly fine with the Text widgets and a few others as well, but when I try to put a image inside it and save the screenshot saves the blank image with no image in it.
Here is the code, and to be clear I'm not trying to save this image only which is already in my assets but with a few other widgets around it.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:screenshot/screenshot.dart';
import 'package:stationary/utils/app_images.dart';
import 'card_screen/components/save_image.dart';
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
Uint8List? _imageFile;
ScreenshotController screenshotController = ScreenshotController();
#override
Widget build(BuildContext context) {
print("buildt");
return Scaffold(
body: Center(
child: Screenshot(
controller: screenshotController,
child: Column(
children: [
Text("Header", style: Theme.of(context).textTheme.headline3),
SizedBox(
child: GestureDetector(
onTap: () {
screenshotController.capture().then((image) {
//Capture Done
setState(() {
_imageFile = image!;
print(_imageFile == null);
});
saveInBrowser(image!);
}).catchError((onError) {
print(onError);
});
},
child: Container(
width: 300.0,
height: 500.0,
child: Image.asset('assets/images/clean-code.png',
fit: BoxFit.cover)),
),
),
Text("I am subtitle place holder",
style: Theme.of(context).textTheme.subtitle1),
],
),
)),
);
}
}
Save In Browser function
void saveInBrowser(Uint8List bytes) {
final dataUri = 'data:text/plain;base64,${base64.encode(bytes)}';
html.document.createElement('a') as html.AnchorElement
..href = dataUri
..download = 'image.png'
..dispatchEvent(html.Event.eventType('MouseEvent', 'click'));
}
I was trying to save the image and texts as the screenshot but got only the widgets and when tried to work with images only got the blank.
You can use image_downloader_web package like this:
Future<void> saveInBrowser(Uint8List bytes) async{
final WebImageDownloader _webImageDownloader = WebImageDownloader();
await _webImageDownloader.downloadImageFromUInt8List(uInt8List: bytes);
}
and also you need to run it in release mode according to this open github issue, run this in terminal:
flutter run -d web-server --web-port 3344 --release --web-renderer canvaskit --dart-define=BROWSER_IMAGE_DECODING_ENABLED=false
also remember to build it with this config too.
I have a flutter project that i added web support to it. Now in my page i have 2 button.I want to when user clicked on each button, a web page shown.So in order to i create custom widget :
#override
Widget build(BuildContext context) {
print("url is ------> ${url}");
isAddedElement();
_iframeElement.height = double.maxFinite.toString();
_iframeElement.width = double.maxFinite.toString();
_iframeElement.src = url;
_iframeElement.style.border = 'none';
_iframeElement.id = 'iframe';
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
_iframeWidget = HtmlElementView(
// key: UniqueKey(),
viewType: 'iframeElement',
);
return SizedBox(
height: 600,
width: 600,
child: _iframeWidget,
);
}
inisAddedElement method i want to check iframeElement.id = 'iframe' is exist or not?If exist i want to change src with the new web page:
bool isAddedElement() {
IFrameElement frame = document.querySelector('iframe');
if (frame != null) frame.src = url;
}
but document.querySelector('iframe') always return null?
The query returns null, most likely because flutter adds the HtmlElementView widget inside a shadowDom and these elements are not available directly in the document scope.
In order to access the element you need to go via a shadowroot which is usually placed under custome tag called flutter-platform-view.
So the following approaches may work.
Accessing via flt-platform-view
Try to access the iframe element as shown below.
document.getElementsByTagName('flutter-platform-view')[0].shadowRoot.getElementById('iframe');
Accessing the IframeElement instance
You already have the reference of IframeElement instance and can check what you need with it as shown below.
bool isAddedElement() {
//IFrameElement frame = document.querySelector('iframe');
if (_iframeElement!= null && _iframeElement.src == null) _iframeElement.src = url;
}
However I don't understand why in your code you are calling the isAddedElement() even before setting up the IframeElement.
You can find more details about the why its put behind a shadow dom in this issue in github.
Here is full working demo using the first approach. The live version is available as a codepen demo
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'dart:html';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: IframeDemo(),
),
),
);
}
}
class IframeDemo extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyWidget();
}
}
class MyWidget extends State<IframeDemo> {
String _url;
IFrameElement _iframeElement;
#override
initState() {
super.initState();
_url = 'https://en.wikipedia.org/wiki/Twitter';
_iframeElement = IFrameElement()
..src = _url
..id = 'iframe'
..style.border = 'none';
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
}
void updateUrl(String page) {
setState(() {
// building a new url
_url = 'https://en.wikipedia.org/wiki/$page';
// Setting the url to the src field of the iframe element.
HtmlElement elem = document.getElementsByTagName('flt-platform-view')[0];
IFrameElement ifrelem = elem.shadowRoot.getElementById('iframe');
ifrelem..src = _url;
});
}
#override
Widget build(BuildContext context) {
print('url is $_url');
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: Colors.blueAccent,
child: Text('Show Twitter wiki'),
onPressed: () {
updateUrl('Twitter');
},
),
SizedBox(
width: 50,
),
MaterialButton(
color: Colors.orangeAccent,
child: Text('Show Facebook Wiki'),
onPressed: () {
updateUrl('Facebook');
},
),
],
),
SizedBox(
height: 100,
),
SizedBox(
height: 600,
width: 600,
child: HtmlElementView(
// key: UniqueKey(),
viewType: 'iframeElement',
),
),
],
);
}
}
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 need quick access to the CameraPreview data for my new flutter app.
If I take a picture with controller.takePicture(filePath) it takes a few seconds for the file to save to the disk, so that I can access it.
I don't need a high quality image, so getting the same resolution as the phone screen display resolution would be fine. I have tried this method, but it only captures the overlays and widgets that I drew myself, not the Camera preview data.
Here is a minimum working example of the problem when using this method:
https://www.youtube.com/watch?v=CWBLjCwH5c0
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'package:camera/camera.dart';
import 'dart:ui' as ui;
List<CameraDescription> cameras;
Future<Null> main() async {
debugPaintSizeEnabled = false;
debugPaintLayerBordersEnabled = false;
try {
cameras = await availableCameras();
} on CameraException catch (e) {
logError(e.code, e.description);
}
runApp(new MaterialApp(
home: new MyApp(),
));
}
void logError(String code, String message) =>
print('Error: $code\nError Message: $message');
class MyApp extends StatefulWidget {
#override
_State createState() => new _State();
}
class _State extends State<MyApp> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
static GlobalKey previewContainer = new GlobalKey();
CameraController controller;
ui.Image image;
Offset blueSquareOffset = new Offset(10.0, 10.0);
#override
void initState() {
super.initState();
controller = new CameraController(cameras[0], ResolutionPreset.low);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
void _getScreenShotImage() async {
_capturePng();
image = await _capturePng();
debugPrint("im height: ${image.height}, im width: ${image.width}");
setState(() {});
}
Future<ui.Image> _capturePng() async {
RenderRepaintBoundary boundary =
previewContainer.currentContext.findRenderObject();
ui.Image image = await boundary.toImage();
return image;
}
/// Display the preview from the camera (or a message if the preview is not available).
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text('Camera is initialising...');
} else {
return Center(
child: new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: RepaintBoundary(
//key: previewContainer,
child: new GestureDetector(
child: new CameraPreview(controller),
),
)),
);
}
}
void _moveBlueSquare(DragUpdateDetails details) {
setState(() {
_getScreenShotImage();
blueSquareOffset = blueSquareOffset + details.delta;
});
}
Widget _blueSquare() {
return new Positioned(
top: blueSquareOffset.dy,
left: blueSquareOffset.dx,
width: 50.0,
height: 50.0,
child: new GestureDetector(
onPanUpdate: _moveBlueSquare,
child: new Container(
color: Color.fromARGB(255, 10, 10, 255),
)));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text('Render Boundry Screenshot Error Example'),
),
body: RepaintBoundary(
key: previewContainer,
child: new Container(
padding: new EdgeInsets.all(0.0),
margin: new EdgeInsets.all(0.0),
child: new RepaintBoundary(
//key: previewContainer,
child: new Stack(
fit: StackFit.expand,
overflow: Overflow.clip,
children: <Widget>[
new Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Expanded(
child: new Stack(children: <Widget>[
new RepaintBoundary(
child: new Container(child: _cameraPreviewWidget()),
),
_blueSquare(),
])),
new Expanded(
child: new Container(
//color: Color.fromARGB(50, 50, 50, 50),
child: new CustomPaint(
painter: new RectanglePainter(image),
)),
)
],
),
],
)))));
}
}
class RectanglePainter extends CustomPainter {
RectanglePainter(this.image);
ui.Image image;
#override
void paint(Canvas canvas, Size size) {
if (image == null) {
canvas.drawRect(
new Rect.fromLTRB(100.0, 50.0, 300.0, 200.0),
new Paint()
..color = Color.fromARGB(255, 50, 50, 255)
..style = PaintingStyle.stroke
..strokeWidth = 6.0);
} else {
canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
}
}
#override
bool shouldRepaint(RectanglePainter old) {
return true;
}
}
Any help would be greatly appreciated.
Update: July 2020
At the moment, the best way to get a screenshot of the CameraPreview on Flutter, is to use the native_screenshot package.
You can simply use,
Future<void> getScreenshot() async{
String path = await NativeScreenshot.takeScreenshot();
print(path);
}
to save the screenshot. Please refer to package page for additional permissions and settings. Performance-wise, it seems to be a bit slow (500ms-1s on my 2018 Xiaomi A1). I am currently looking at ways to improve the screen capture speed.
There is a way to take snapshot from layouts. try this:
Android get Image of Main Relativelayout from xml layout?
I hope this helps to you.