I'm filtering my list of my GridView.builder like this:
onSearchTextChanged(String text) async {
if (text.isEmpty) {
_searchList = _productList.toList();
setState(() {
});
return;
}
_searchList = _productList.where((product) => product.name.toLowerCase().contains(text.toLowerCase()) ||
product.type.toLowerCase().contains(text.toLowerCase())).toList();
setState(() {});
}
but when im typing on the textfield the performance just go down, exactly to 2.5 fps sometimes when im deleting the text or typing to fast.
This is my Gridview builder
GridView.builder(
primary: false,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: itemWidth / itemHeight,
),
itemCount: _searchList.length,
itemBuilder: (BuildContext context, int index) {
return _searchList[index];
}));
Always set state variables inside the setState() call.
Make your onSearchTextChanged() function save the search text and start a Future.delayed() that performs the search and update after a short delay. See: https://stackoverflow.com/a/54686588/1736338 .
You can use a Suffix Tree data structure to speed up the search operation. Unfortunately, I couldn't find one written in Dartlang. You could code one. The trie package is unsuitable since it supports only Set functionality, not Map functionality.
Related
When I move from tabs in the UI it's takes too long to load the chips under the tabs.
I've used the AutomaticKeepAliveClientMixin<> but it broke my Scrollbar and still takes time to load large Chips children from the Wrap Widget. What I would need is a Wrap.builder() but doesn't exist. Is there something I can do to optimize the code to load smoothly?
I have an interactive sample code of the current problem:
https://dartpad.dev/009e9ccae07175074cb77d7792c3692b
Try moving the tabs left to right in the sample to see the performance issue.
If it can't be fixed, I wonder if I can detect when the widget is rendering to show a circular progress indicator while switching tabs.
Any Ideas?
Thanks
Technically the idea of Wrap.builder wouldn't be a solution here because the issue persists even when all the items are in the displayed (nothing to display on demand)
You can make the swiping experience better by delaying the rendering of the tab content until after the swiping animation is done
class _ChipsContentState extends State<ChipsContent> {
bool visible = false;
final List<String> _filters = <String>[];
#override
void initState() {
super.initState();
Future.delayed(kTabScrollDuration).then((value) {
if (mounted) {
setState(() => visible = true);
}
});
}
#override
Widget build(BuildContext context) {
if (!visible) {
return Center(child: CircularProgressIndicator());
}
return ListView(
children: [
if (visible)
Wrap(
spacing: 8.0,
children: widget.chipData.map((chipName) => ChipFilter(chipName: chipName, filter: _filters)).toList(),
),
],
);
}
}
A Wrap.builder() is indeed missing from Flutter. A GitHub issue was opened about this but then closed without a satisfying solution.
I can think of a few workarounds, like using a swiper/carousel, a GridView.builder(), or a List.builder() with Rows inside it. Of course, they're far from being as convenient as a Wrap in this case.
Try Changing your filterChips and "build" methods as follows,
List<Widget> get filterChips {
List<Widget> widgets = [];
for (final chipName in widget.chipData) {
widgets.add(ChipFilter(chipName: chipName, filter: _filters));
}
return widgets;
}
#override
Widget build(BuildContext context) {
// super.build(context);
return Scrollbar(
child: SingleChildScrollView(
child: Wrap(//IS THERE A Wrap.builder()? OR ALTERNATIVE TO DESIRED LAYOUT?
spacing: 8.0,
children: filterChips,
),),
);
}
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.
So I have a normal TextField with which I filter a list
children: products.map((doc) => _buildSingleProduct(doc)).toList(),, it works as it should, then I added to it a text-to-speech pluugin speech_recognition: and the combined it with the filtering function, It works all fine.
The problem is when I finish with the speech filtering then I for example want to add or make corrections to it with writing to the TextField it doesn't filter anymore.
Textfield
child: TextField(
controller: controller,
decoration: InputDecoration(
labelText: allTranslations.text(StringConstant.search),
prefixIcon: Icon(Icons.search),
suffixIcon: IconButton(
icon: Icon(Icons.mic),
onPressed: () {
if (_isAvailable && !_isListening)
_speechRecognition
.listen(locale: "en_US")
.then((result) => print('$result'));
},
),
),
),
As you can see there is the controller which I use to filter, and then the mic icon to pass the result from the speech to the controller like this:
_speechRecognition
.setRecognitionResultHandler((String result) => setState(() {
controller = TextEditingController(text: resultText = result);
}));
here I get the result from the speech and add it to the resultText of the filter and the controller so it appears in the textField.
if I do it like this:
_speechRecognition
.setRecognitionResultHandler((String speech) => setState(() => resultText = speech));
it works all fine but the text does not appear in the text-field obviously.
for the textField filtering I init the state to add it to the resultText:
initState() {
initSpeechRecognizer();
controller.addListener(() {
setState(() {
resultText = controller.text;
});
});
super.initState();
}
this is how I return the result from the db:
return resultText == null || resultText == ""
? buildProducts(id, title, favorite, message, price, doc)
: doc.data['title'].toLowerCase().contains(resultText.toLowerCase())
? buildProducts(id, title, favorite, message, price, doc)
: Container();
as you can probably see I search the title.
So the problem one more time,
1.search with speech
it appears on the textField and filters the list
when I press the textField to change the query it doesn't filter anymore.
But the other way around works
filter the list with text
it filters the list
I activate speech-to-text and it changes the query and filters the list with the new query.
So for people who need the solution
_speechRecognition
.setRecognitionResultHandler((String result) => setState(() {
resultText = result;
controller.text = resultText;
}));
you get the result from the speech, add this to the handling variable, but you also than add that result to the controller so you get the result at the textField.
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());
}
},
),
);
}
I've followed the various animation tutorials on flutter.io, (tween, stagger, transitions, etc.) and its all great.
What I would like to explore is how to actually make custom animations based on the composition of a UI object.
Lets take a simple example, a Pause -> Play animation.
At first we have a Pause icon, two vertical bars.
Let's say I would like to
Grow the right bar into a triangle by adding an extra corner on the center of the rightmost vertical side, and moving it to the right.
After that moving that triangle from step 1 slightly to the left, so it now sticks to the leftmost vertical bar, into a bigger "triangle" (that'd be a pentagon actually).
That would look like a play button, and not a pause button anymore.
How would I achieve that kind of custom animation ? I'm assuming I can't work with the icons class. And I'm pretty sure I shouldn't do that with Widgets and just move them around.
Where would I go to start exploring that kind of precision in animations?
The answer from #Alaric points you at a couple of packages but doesn't really give any justification for why you'd use them.
The issue at hand is that the animation you're talking about is moderately complicated in terms of how it actually works. There are multiple items which change over time and possibly even become one bigger item.
There are two approaches you could take to solving this problem. The first is to use an external animation tool to create this animation, using whichever features the animation tool has to do item changing and merging. Then once you have an animation which runs to your satisfaction, you have to import it into your project somehow. That's where the fluttie and flare_flutter plugins come in - if you used Aftereffects, you use Lottie to export the file and then the fluttie plugin to show it. Flare is slightly simpler as it's meant for flutter, but you still create the animation externally and then add the file to your assets to be rendered.
The other approach is to do the animation yourself. That entails three things:
Creating a widget to contain the animation.
Creating a CustomPainter to actually draw the result.
Optionally, another class which acts as controller to start/stop/etc the animation.
The widget containing the animation could probably also be the controller if you use a GlobalKey to access it and expose start/stop methods, but that's a bit messy. It's better to have an external object that is the controller - and you could probably even use an AnimationController as-is although it would be less 'clean'.
If you don't pass it in, you'd probably have an AnimationController in your widget that you start and stop from your controller or class. It would essentially just go from 0 to 1 and back, and would be responsible for rebuilding the CustomPainter (probably using an AnimatedBuilder).
This is a very basic example that doesn't need an external controller as the gesture detection happens within the widget. Note that I'm not calling setState every time the 'started' member is set, because I don't actually want it to rebuild when it changes.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StartStop(),
),
),
);
}
}
class StartStop extends StatefulWidget {
#override
StartStopState createState() {
return new StartStopState();
}
}
class StartStopState extends State<StartStop> with TickerProviderStateMixin<StartStop> {
bool started = false;
AnimationController animationController;
#override
void initState() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
started ? animationController.forward() : animationController.reverse();
started = !started;
},
child: SizedBox(
width: 100,
height: 100,
child: AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return CustomPaint(
painter: StartStopPainter(animationController.value),
size: Size.infinite,
child: child,
);
},
),
),
);
}
}
class StartStopPainter extends CustomPainter {
final double percentAnimated;
StartStopPainter(this.percentAnimated) : assert(percentAnimated >= 0 && percentAnimated <= 1);
#override
void paint(Canvas canvas, Size size) {
var pausePaint = Paint()..color = Colors.black.withOpacity(1 - percentAnimated);
canvas.drawRect(Rect.fromLTRB(20, 10, 40, 90), pausePaint);
canvas.drawRect(Rect.fromLTRB(60, 10, 80, 90), pausePaint);
var playPaint = Paint()..color = Colors.black.withOpacity(percentAnimated);
canvas.drawPath(Path()..addPolygon([Offset(20, 10), Offset(20, 90), Offset(80, 50)], true), playPaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I'll leave the actual custom part of the animation (where you make the rectangle change to a triangle etc) to you. Instead of using opacity and a few different paint calls, you'd simply be using the input percentAnimated to decide which path or polygon to draw.