flutter bloc and navigation drawer - navigation-drawer

New to Bloc and love having a structured app!
I used the firebase login example:
Flutter Firebase Login Tutorial
Works great and great example. Building upon this example I decided to do it step by step (avoid big mistakes).
I created a bottom navigation with an appbar that has a logout icon. Clicked on logout and it works.
Now I wanted to move the logout to the navigation drawer. I created a statelessWidget dart file called DrawerItems and then I call it from my bottom navigation file (within my scaffold). Here is part of the code of the DrawerItems:
DrawerListItem(
navListIcon: const Icon(Icons.logout_sharp),
strTitle: "Logout",
myFunction: () => context.read<AppBloc>().add(AppLogoutRequested()),
),
When I click on the logout icon, it doesn't do anything. Do I really need to make this statefulWidget? the whole point of bloc is to use stateful widgets as little as possible no?
any insight will be great!
Thank you in advance :D

Ok I figured it out... not really happy about this answer (I like having different files for everything), so if any of you know why this is happening feel free to drop a comment!
I removed the "myFunction" that I was passing and put the read bloc line directly in the drawer stateless class and it worked.
so this is my final code (I kept the old code there too FYI):
Widget build(BuildContext context) {
const drawerHeader = UserAccountsDrawerHeader(
accountName: Text("Account Name"),
accountEmail: Text("Account Email"),
currentAccountPicture: CircleAvatar(
child: FlutterLogo(size: 42.0),
),
);
return ListView(
children: [
drawerHeader,
// DrawerListItem(
// navListIcon: Icon(Icons.account_circle_sharp),
// strTitle: "Account information",
// ),
// DrawerListItem(
// navListIcon: Icon(Icons.filter_list_sharp),
// strTitle: "Filter",
// ),
ListTile(
leading: const Icon(Icons.logout_sharp),
title: const Text("Logout"),
onTap: () {
context.read<AppBloc>().add(AppLogoutRequested());
Navigator.pop(context);
},
),
// DrawerListItem(
// navListIcon: const Icon(Icons.logout_sharp),
// strTitle: "Logout",
// myFunction: () => context.read<AppBloc>().add(AppLogoutRequested()),
// ),
],
);
}

Related

Can I run "image.dart" functions in background

I want to display a loading widget while compressing the image selected.
I am using the "image.dart" package to select an image, using ImagePicker.GetImage().
Then I want to display a loading widget while the image is being compressed, and when it's finished display the compressed image.
My code fundamentally works, my loading widget is cool (thanks spinkit), I can select any image, compress it and display the new one, but there is a small freeze (2-3s) while the image is being compressed.
So I'm trying to put a loading widget to not make the user panic and tap 10 000 times to select the image wanted, but I'm not completely comfortable with asynchronous code yet.
Here's my code :
import 'package:image/image.dart' as Img;
#override
Widget build(BuildContext context) {
if (loading == true) {
return LoadingScreen();
} else {
return Scaffold(
backgroundColor: Colors.brown[300],
body: Column(
children: [
SizedBox(
height: 50,
),
RaisedButton(
child: Text('Select an image'),
onPressed: () async {
await getImage();
// You can see here that I'm trying to trigger the loading widget
setState(() {
loading = true;
});
compressImage();
// And I want to disable the loading widget here, after the compression
setState(() {
loading = false;
});
},
),
SizedBox(
height: 10,
),
pickedImage != null ? printSelectedImage() : Center(child: Text('Please select an image')),
],
),
);
}
}
getImage() async {
pickedFile = await picker.getImage(source: ImageSource.gallery);
}
compressImage() async {
Img.Image selectedImage = Img.decodeJpg(File(pickedFile.path).readAsBytesSync());
if (selectedImage.width > selectedImage.height) {
Img.Image compressedImage = Img.copyResize(selectedImage, width: 500);
File(pickedFile.path).writeAsBytesSync(Img.encodePng(compressedImage));
} else {
Img.Image compressedImage = Img.copyResize(selectedImage, height: 500);
File(pickedFile.path).writeAsBytesSync(Img.encodePng(compressedImage));
}
if (pickedFile.path != null) {
setState(() {
pickedImage = pickedFile.path;
});
}
}
But the result is the same, the screen is still stuck in the file selector while compressing, and then directly display the compressed image.
I started learning dart/Flutter 2 weeks ago, so am I missing some fundamental principles of the dart language ? Am I not seeing something obvious ?
Thanks for reading
Making something async doesn't move it into some magical background thread. Dart uses isolates which are basically an execution context in which dart code can run. Flutter has a single isolate which runs your app, if you do too much work in it, your app gets slow or skips frames.
You can create other isolates to do calculations and offload work and communicate between isolates by passing messages with primitive content back and forth. The drawback in Flutter is, that other isolates don't have access to Flutter plugins thus a lot of things don't work there.
There are ways to work around this but they are pretty involved.
The easiest way to start an isolate is the compute(callback, message) function.
Spawn an isolate, run callback on that isolate, passing it message, and
(eventually) return the value returned by callback.
I am not sure if the image/image.dart library uses a plugin or some other features that are not available (dart:ui for example), you can try offloading your image compression into the callback. If it fails with a MissingPluginException or something similar, then it won't work.
Remember, you can only pass primitive messages so you can not path the pickedFile, only the pass as argument.
For your problem however there may be an easier solution. The image picker accepts parameters for max width/height and quality. If you need the image to be encoded as PNG however then you need to create a complex solution yourself.

fluttercandies extended_image. How do I zoom in on an image with double tap of 1 finger instead of needing 2?

https://github.com/fluttercandies/extended_image
https://github.com/fluttercandies/extended_image/tree/master/example
https://github.com/fluttercandies/extended_image/blob/master/example/lib/pages/zoom_image_demo.dart
fluttercandies extended_image. How do I zoom in on an image with double tap of 1 finger instead of needing 2? How do I get a sliding image to navigator pop back when it slides off the page?
Trying to implement a zoom/pan image with double tap by 1 finger instead of the need for 2 to expand zoom an image. Have two issues with this code and was wondering if anyone has any ideas. The class is very simple with just 2 strings: image & title passed into it.
1, I need the image to expand on double tap. I would like the user to have to power to expand the image with one finger and not two. Think I need to put this code near the very end.
The good thing is that once it is expanded the double tap works to reduce the image size. How do I get it to do the opposite when it is at normal size?
2, the sliding of the image off the page results in a black screen. Thankfully, this doesn’t freeze or crash the app but it leaves the user with a blank screen and the need to press the system back button. I would like the slide to result in a navigator pop back to the original screen.
Firstly, here’s a sample code of how I’m passing an image and a title into expandimage.dart.
FlatButton(
            child: Image.asset(_kAsset5),
            onPressed: () async {
              Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) => ExpandImage(
                          image: _kAsset5,
                          title: "\'go help\' 1",
                        )),
              );
            },
          ),
Here’s the code that I’m using for this ‘expandimage.dart’ and a lot of it is based on the pan/zoom example from flutter candies / extended image example.
import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
class ExpandImage extends StatelessWidget {
  final String image, title;
  ExpandImage({this.image, this.title});
  #override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.red[900],
        appBar: AppBar(
          backgroundColor: Colors.red[900],
          leading: IconButton(
            icon: Icon(Icons.close),
            onPressed: Navigator.of(context).pop,
          ),
          title: Text(
            title,
            style: TextStyle(
              color: Colors.yellow,
              inherit: true,
              fontWeight: FontWeight.w300,
              fontStyle: FontStyle.italic,
              letterSpacing: 2.0, //1.2
            ),
          ),
          centerTitle: true,
        ),
        body: SizedBox.expand(
          // child: Hero(
          // tag: heroTag,
          child: ExtendedImageSlidePage(
            slideAxis: SlideAxis.both,
            slideType: SlideType.onlyImage,
            child: ExtendedImage(
              //disable to stop image sliding off page && entering dead end without back button.
//setting to false means it won't slide at all.
              enableSlideOutPage: true,
              mode: ExtendedImageMode.gesture,
              initGestureConfigHandler: (state) => GestureConfig(
                minScale: 1.0,
                animationMinScale: 0.8,
                maxScale: 3.0,
                animationMaxScale: 3.5,
                speed: 1.0,
                inertialSpeed: 100.0,
                initialScale: 1.0,
                inPageView: false,
              ),
              // onDoubleTap: ? zoom in on image
              fit: BoxFit.scaleDown,
              image: AssetImage(
                image,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
Here is a sample image passed in. the page turns red when sliding the image around and then it goes black as the image slides off the page.
Hope it will help, if have any doubt ask in the comments.
class _DetailState extends State<Detail> with TickerProviderStateMixin{
#override
Widget build(BuildContext context) {
AnimationController _animationController = AnimationController(duration: Duration(milliseconds: 200),vsync: this);
Function() animationListener = () {};
Animation? _animation;
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
child: ExtendedImage.network(
widget.wallpaper.path,
fit: BoxFit.contain,
mode: ExtendedImageMode.gesture,
initGestureConfigHandler: (state) {
return GestureConfig(
minScale: 0.9,
animationMinScale: 0.7,
maxScale: 3.0,
animationMaxScale: 3.5,
speed: 1.0,
inertialSpeed: 100.0,
initialScale: 1.0,
inPageView: false,
initialAlignment: InitialAlignment.center,
);
},
onDoubleTap: (ExtendedImageGestureState state) {
///you can use define pointerDownPosition as you can,
///default value is double tap pointer down postion.
var pointerDownPosition = state.pointerDownPosition;
double begin = state.gestureDetails!.totalScale!;
double end;
_animation?.removeListener(animationListener);
_animationController.stop();
_animationController.reset();
if (begin == 1) {
end = 1.5;
} else {
end = 1;
}
animationListener = () {
//print(_animation.value);
state.handleDoubleTap(
scale: _animation!.value,
doubleTapPosition: pointerDownPosition);
};
_animation = _animationController
.drive(Tween<double>(begin: begin, end: end));
_animation!.addListener(animationListener);
_animationController.forward();
},
),
),
}
}
If it helps, mark it as right.

Hero animation starting from side menu drawer and ending on destination screen in Flutter

I'm trying to do a Hero animation that starts from side menu drawer's item (it's Hero widget) and ends up on new screen's Hero widget. For example, on image shown below, a pie-chart icon should animate to the final widget when destination route is shown:
Each screen contains it's own scaffold with shared drawer widget instance:
Scaffold(
...
drawer: MyAppDrawer();
)
So for each of the screens and corresponding drawer items, I have the Hero tag on the screen that equals to the side menu drawer item Hero tag.
The destination screen is replacing the current screen like so:
Navigator.of(context).pushReplacementNamed("/");
After trying to push replacement screen on menu item action, instead of hero animation, I'm getting the following error:
There are multiple heroes that share the same tag within a subtree.
I guess it's because each of the screens already contains identical MyAppDrawer (with the same Hero tags)... But I can't omit the drawer from other screens because i need that menu to be globally accessible.
Is Hero animation from the common app side menu drawer item to destination screen Hero widget possible?
Any experiences with showing Hero animation from the drawer to the screen?
Thanks!
The problem is that you are not sharing the drawer with all the screens you are for each screen creating a new MyAppDrawer. Thinking fast here you could create the MyAppDrawer somewhere else and import the same object onto every screen
I have already facing this hero problem in my project.
If you put same hero tag like
tag:1 , tag :1, tag :1
then you will get this kind of error
tag:2 , tag:3 , tag:4
You will get your perfect solution through this trick.
and remembar that every hero tag must be unique in one screen.
And If you have using flotingActionButton then you have to put define tag inside button.
GestureDetector(
onTap: () => Navigator.push(
context,
PageRouteBuilder(
transitionDuration:
Duration(milliseconds: 700),
pageBuilder: (_, __, ___) => StoryInside(
tag: "Story $index",
))),
child: Hero(
tag: 'Story $index',
child: Material(
animationDuration: Duration(seconds: 20),
color: Colors.transparent,
child: ListTile(
title: Text(
'Story $index ',
style: style,
),
),
),
)),
Hero(
tag: '${widget.tag}',
transitionOnUserGestures: true,
child: Text(
'${widget.tag}',
style: style,
),
),

How to create a picture area in Flutter?

In their example https://flutter.dev/docs/cookbook/plugins/picture-using-camera we can take pictures using the camera plugin.
I modified the code and move into a separate widget. My main goal is to try to implement a picture area (like QRCode style) and take the picture and if necessary tell the user to corp the image or my be app will do it automatically. There is a barcode_scan plugin. It shows an area to scan the barcode. I like to implement that part to take pictures of an item.
https://codecanyon.net/item/qr-code-barcode-scanner-and-generator-for-ios-swift-with-admob/screenshots/20280521?index=1 screenshot2 has a 4 half square bracket on 4 edges. I like to make it similar but capture images only.
I am new to this plugin and corp idea. How can I create a picture area in Flutter so the user can center the item in that area and take a picture.?
Widget _buildPictureArea(BuildContext context) {
return new Container(
width: 200,
height: 200,
child: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator
return Center(child: CircularProgressIndicator());
}
},
),
);
}

onTapUp isn't called after onLongPress

I'm trying to create a context menu that is displayed after a long press whilst keeping your finger held on the object, afterwards you would select an option (by dragging your finger) and lift it to initiate the action.
I noticed something that to me is a little strange. So first, the code:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Scaffold(
body: GestureDetector(
onLongPress: () {
print("onLongPress");
},
onTapUp: (TapUpDetails details) {
print("onTapUp");
},
child: Center(
child: FlutterLogo(),
),
),
),
));
}
So if I run this, after long pressing the icon, then the handler for onLongPress is called but after lifting your finger onTapUp isn't called. If I comment out the handler for onLongPress (all 3 lines) then after a long press onTapUp is called. I would like to handle both onTapUp and onLongPress.
Any ideas why this is happening?
You can wrap in another gesture detector that will handle tap up.
I am not sure if current behavior is a bug, so you might want to submit it to issue tracker.
I don't know if the PR will be pass but I've solved it with a new gesture onLongPressUp.
Here is it https://github.com/flutter/flutter/pull/18949

Resources