Skip to main content

48 posts tagged with "Dart"

View All Tags

A GUIDE ON FLUTTER ANIMATIONS

Published: · Last updated: · 11 min read
Appxiom Team
Mobile App Performance Experts

Animations play a vital role in creating engaging and visually appealing user interfaces in mobile applications. Flutter, a popular open-source UI framework by Google, offers a robust set of tools for creating smooth and expressive animations.

In this comprehensive guide, we'll explore the world of Flutter animations, from the basics to more advanced techniques, accompanied by code samples to help you get started.

1. Introduction to Flutter Animations

Why Animations Matter

Animations provide a more dynamic and engaging user experience, guiding users through interface changes and interactions. They help convey important information, enhance the overall aesthetic of the app, and make interactions more intuitive.

Types of Animations in Flutter

Flutter offers several animation types:

  • Implicit Animations: These animations are built into existing widgets and can be triggered using widget properties, like AnimatedContainer or AnimatedOpacity.

  • Tween Animations: These animations interpolate between two values over a specified duration using Tween objects.

  • Physics-Based Animations: These animations simulate real-world physics, like springs or flings, to create natural-looking motion.

  • Custom Animations: For more complex scenarios, you can create your own custom animations using CustomPainter and AnimationController.

In this guide, we'll cover examples from each category to give you a well-rounded understanding of Flutter animations.

2. Basic Animations

Animated Container

The AnimatedContainer widget is a straightforward way to animate changes to a container's properties, such as its size, color, and alignment.

class BasicAnimatedContainer extends StatefulWidget {
@override
_BasicAnimatedContainerState createState() => _BasicAnimatedContainerState();
}

class _BasicAnimatedContainerState extends State<BasicAnimatedContainer> {
double _width = 100.0;
double _height = 100.0;
Color _color = Colors.blue;

void _animateContainer() {
setState(() {
_width = 200.0;
_height = 200.0;
_color = Colors.red;
});
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _animateContainer,
child: Center(
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _width,
height: _height,
color: _color,
),
),
);
}
}

Animated Opacity

The AnimatedOpacity widget allows you to animate the opacity of a widget, making it appear or disappear smoothly.

class BasicAnimatedOpacity extends StatefulWidget {
@override
_BasicAnimatedOpacityState createState() => _BasicAnimatedOpacityState();
}

class _BasicAnimatedOpacityState extends State<BasicAnimatedOpacity> {
bool _visible = true;

void _toggleVisibility() {
setState(() {
_visible = !_visible;
});
}

@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedOpacity(
duration: Duration(seconds: 1),
opacity: _visible ? 1.0 : 0.0,
child: FlutterLogo(size: 150.0),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _toggleVisibility,
child: Text(_visible ? "Hide Logo" : "Show Logo"),
),
],
);
}
}

3. Tween Animations

Animating Widgets with Tween

Tween animations interpolate between two values over a specified duration. Here's an example of animating the position of a widget using a Tween:

class TweenAnimation extends StatefulWidget {
@override
_TweenAnimationState createState() => _TweenAnimationState();
}

class _TweenAnimationState extends State<TweenAnimation> {
double _endValue = 200.0;

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_endValue = _endValue == 200.0 ? 100.0 : 200.0;
});
},
child: Center(
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 100.0, end: _endValue),
duration: Duration(seconds: 1),
builder: (BuildContext context, double value, Widget? child) {
return Container(
width: value,
height: value,
color: Colors.blue,
);
},
),
),
);
}
}

Tween Animation Builder

The TweenAnimationBuilder widget is a versatile tool for building animations with Tweens. It allows you to define the tween, duration, and a builder function to create the animated widget.

4. Physics-Based Animations

Using AnimatedBuilder with Curves

Curves define the rate of change in an animation, affecting its acceleration and deceleration. The CurvedAnimation class allows you to apply curves to your animations. Here's an example of using AnimatedBuilder with a curve:

class CurvedAnimationDemo extends StatefulWidget {
@override
_CurvedAnimationDemoState createState() => _CurvedAnimationDemoState();
}

class _CurvedAnimationDemoState extends State<CurvedAnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
final Animation curveAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
_animation = Tween<double>(begin: 0, end: 200).animate(curveAnimation);
_controller.forward();
}

@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget? child) {
return Container(
width: _animation.value,
height: 100,
color: Colors.blue,
);
},
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

Creating a Spring Animation

Spring animations simulate the behavior of a spring, creating a bounce-like effect. Flutter provides the SpringSimulation class for this purpose. Here's an example of creating a spring animation:

class SpringAnimationDemo extends StatefulWidget {
@override
_SpringAnimationDemoState createState() => _SpringAnimationDemoState();
}

class _SpringAnimationDemoState extends State<SpringAnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);

final SpringDescription spring = SpringDescription(
mass: 1,
stiffness: 500,
damping: 20,
);

final SpringSimulation springSimulation = SpringSimulation(
spring,
_controller.value,
1, // The target value
0, // The velocity
);

_animation = Tween<Offset>(begin: Offset.zero, end: Offset(2, 0))
.animate(_controller);

_controller.animateWith(springSimulation);
}

@override
Widget build(BuildContext context) {
return Center(
child: SlideTransition(
position: _animation,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

5. Complex Animations

Staggered Animations

Staggered animations involve animating multiple widgets with different delays, creating an appealing sequence. The StaggeredAnimation class manages this behavior. Here's an example:

class StaggeredAnimationDemo extends StatefulWidget {
@override
_StaggeredAnimationDemoState createState() => _StaggeredAnimationDemoState();
}

class _StaggeredAnimationDemoState extends State<StaggeredAnimationDemo>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);

final StaggeredAnimation staggeredAnimation = StaggeredAnimation(
controller: _controller,
itemCount: 3,
);

_animation = Tween<double>(begin: 0, end: 200).animate(staggeredAnimation);

_controller.forward();
}

@override
Widget build(BuildContext context) {
return Center(
child: ListView.builder(
itemCount: 3,
itemBuilder: (BuildContext context, int index) {
return FadeTransition(
opacity: _animation,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: _animation.value,
height: 100,
color: Colors.blue,
),
),
);
},
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

class StaggeredAnimation extends Animatable<double> {
final AnimationController controller;
final int itemCount;

StaggeredAnimation({
required this.controller,
required this.itemCount,
}) : super();

@override
double transform(double t) {
int itemCount = this.itemCount;
double fraction = 1.0 / itemCount;
return (t * itemCount).clamp(0.0, itemCount - 1).toDouble() * fraction;
}
}

Hero Animations

Hero animations are used to smoothly transition a widget between two screens. They provide a seamless experience as the widget scales and moves from one screen to another. Here's an example:

class HeroAnimationDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Hero Animation'),
),
body: Center(
child: Hero(
tag: 'hero-tag',
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
),
);
},
),
);
},
child: Scaffold(
appBar: AppBar(
title: const Text('Hero Animation'),
),
body: Center(
child: Hero(
tag: 'hero-tag',
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
),
),
);
}
}

6. Implicit Animations

Animated CrossFade

The AnimatedCrossFade widget smoothly transitions between two children while crossfading between them. It's useful for scenarios like toggling between two pieces of content.

class CrossFadeDemo extends StatefulWidget {
@override
_CrossFadeDemoState createState() => _CrossFadeDemoState();
}

class _CrossFadeDemoState extends State<CrossFadeDemo> {
bool _showFirst = true;

void _toggle() {
setState(() {
_showFirst = !_showFirst;
});
}

@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedCrossFade(
firstChild: FlutterLogo(size: 150),
secondChild: Container(color: Colors.blue, width: 150, height: 150),
crossFadeState:
_showFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond,
duration: Duration(seconds: 1),
),
ElevatedButton(
onPressed: _toggle,
child: Text(_showFirst ? 'Show Second' : 'Show First'),
),
],
);
}
}

Animated Switcher

The AnimatedSwitcher widget allows smooth transitions between different children based on a key. It's commonly used for transitions like swapping widgets.

class SwitcherDemo extends StatefulWidget {
@override
_SwitcherDemoState createState() => _SwitcherDemoState();
}

class _SwitcherDemoState extends State<SwitcherDemo> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSwitcher(
duration: Duration(seconds: 1),
child: Text(
'$_counter',
key: ValueKey<int>(_counter),
style: TextStyle(fontSize: 48),
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}

7. Custom Animations

CustomPainter and AnimationController

The combination of CustomPainter and AnimationController allows you to create complex animations and draw custom shapes. Here's an example of a rotating custom animation using CustomPainter:

class CustomPainterAnimation extends StatefulWidget {
@override
_CustomPainterAnimationState createState() => _CustomPainterAnimationState();
}

class _CustomPainterAnimationState extends State<CustomPainterAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..repeat();
}

@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
return CustomPaint(
painter: RotatingPainter(_controller.value),
child: Container(width: 150, height: 150),
);
},
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

class RotatingPainter extends CustomPainter {
final double rotation;

RotatingPainter(this.rotation);

@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
canvas.rotate(rotation * 2 * pi);
final rect = Rect.fromCenter(
center: Offset(0, 0),
width: size.width * 0.8,
height: size.height * 0.8,
);
final paint = Paint()..color = Colors.blue;
canvas.drawRect(rect, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

Creating a Flip Card Animation

Using a combination of Transform, GestureDetector, and AnimationController, you can create a flip card animation.

class FlipCardDemo extends StatefulWidget {
@override
_FlipCardDemoState createState() => _FlipCardDemoState();
}

class _FlipCardDemoState extends State<FlipCardDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
bool _isFront = true;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500),
);
}

void _flipCard() {
if (_isFront) {
_controller.forward();
} else {
_controller.reverse();
}
_isFront = !_isFront;
}

@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: _flipCard,
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
final double rotationValue = _controller.value;
final double rotationAngle = _isFront ? rotationValue : (1 - rotationValue);
final frontRotation = Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(pi * rotationAngle);
final backRotation = Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(pi * (rotationAngle - 1));
return Stack(
children: [
_buildCard(frontRotation, 'Front', Colors.blue),
_buildCard(backRotation, 'Back', Colors.red),
],
);
},
),
),
);
}

Widget _buildCard(Matrix4 transform, String text, Color color) {
return Center(
child: Transform(
transform: transform,
alignment: Alignment.center,
child: Container(
width: 200,
height: 300,
color: color,
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(fontSize: 24, color: Colors.white),
),
),
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

8. Performance Optimization

Using the AnimationController's vsync

When creating an AnimationController, it's essential to provide a vsync parameter. This parameter helps in syncing the animation frame rate with the device's refresh rate, enhancing performance and reducing unnecessary updates.

class VsyncAnimation extends StatefulWidget {
@override
_VsyncAnimationState createState() => _VsyncAnimationState();
}

class _VsyncAnimationState extends State<VsyncAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this, // Pass `this` as the vsync parameter
duration: Duration(seconds: 2),
);
}

// ...
}

Avoiding Unnecessary Rebuilds

To avoid unnecessary rebuilds of widgets, you can use AnimatedBuilder or ValueListenableBuilder. These widgets rebuild only when the animation value changes, improving overall performance.

class AvoidRebuildsDemo extends StatefulWidget {
@override
_AvoidRebuildsDemoState createState() => _AvoidRebuildsDemoState();
}

class _AvoidRebuildsDemoState extends State<AvoidRebuildsDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);

_animation = Tween<double>(begin: 0, end: 1).animate(_controller);
}

@override
Widget build(BuildContext context) {
return Center(
child: ValueListenableBuilder(
valueListenable: _animation,
builder: (BuildContext context, double value, Widget? child) {
return Transform.scale(
scale: value,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
},
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

9. Chaining and Sequencing Animations

Using Future.delayed

You can chain animations by using Future.delayed. This creates a delayed effect, allowing one animation to start after the previous one completes.

class DelayedAnimationDemo extends StatefulWidget {
@override
_DelayedAnimationDemoState createState() => _DelayedAnimationDemoState();
}

class _DelayedAnimationDemoState extends State<DelayedAnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation1;
late Animation<double> _animation2;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);

_animation1 = Tween<double>(begin: 0, end: 1).animate(_controller);

_animation2 = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.5, 1.0), // Starts after the first animation
),
);

_controller.forward();
}

@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScaleTransition(
scale: _animation1,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
SizedBox(height: 20),
ScaleTransition(
scale: _animation2,
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
],
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

Using AnimationController's addListener

The addListener method of AnimationController can be used to sequence animations, triggering the second animation when the first animation completes.

class SequenceAnimationDemo extends StatefulWidget {
@override
_SequenceAnimationDemoState createState() => _SequenceAnimationDemoState();
}

class _SequenceAnimationDemoState extends State<SequenceAnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation1;
late Animation<double> _animation2;

@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);

_animation1 = Tween<double>(begin: 0, end: 1).animate(_controller)
..addListener(() {
if (_animation1.isCompleted) {
_controller.reset(); // Reset the controller to restart
_controller.forward(); // Start the second animation
}
});

_animation2 = Tween<double>(begin: 0, end: 1).animate(_controller);

_controller.forward();
}

@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScaleTransition(
scale: _animation1,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
SizedBox(height: 20),
ScaleTransition(
scale: _animation2,
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
],
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

10. Conclusion and Further Learning

Flutter's animation capabilities allow you to create stunning, dynamic user interfaces that engage users and enhance their experience. This guide covered a wide range of animation techniques, from basic animations and tween animations to physics-based simulations and complex custom animations.

As you continue your journey with Flutter animations, consider exploring more advanced topics like Flare animations for vector graphics, using Rive for more complex animations, and experimenting with implicit animations for seamless UI changes.

Remember, mastering Flutter animations takes practice and experimentation. With dedication and creativity, you can bring your app's UI to life and create memorable user experiences that leave a lasting impression.

Happy animating! 🚀

Note: The code samples provided in this blog post are simplified for illustrative purposes. Actual implementation may require additional considerations and optimizations.

HOW TO INTEGRATE PUSH NOTIFICATIONS IN FLUTTER USING FIREBASE

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Push notifications are a crucial component of modern mobile applications, allowing you to engage and re-engage users by sending timely updates and reminders.

In this blog post, we'll explore how to integrate push notifications in a Flutter app using Firebase Cloud Messaging (FCM). Firebase Cloud Messaging is a powerful and user-friendly platform that enables sending notifications to both Android and iOS devices.

Prerequisites

Before we begin, ensure that you have the following prerequisites in place:

  • Flutter Development Environment: Make sure you have Flutter and Dart installed on your system. If not, follow the official Flutter installation guide: Flutter Installation Guide

  • Firebase Project: Create a Firebase project if you haven't already. Visit the Firebase Console (https://console.firebase.google.com/) and set up a new project.

Step 1: Set Up Firebase Project

  • Go to the Firebase Console and select your project.

  • Click on "Project settings" and then navigate to the "Cloud Messaging" tab.

  • Here, you'll find your Server Key and Sender ID. These will be used later in your Flutter app to communicate with Firebase Cloud Messaging.

Step 2: Add Firebase Dependencies

In your Flutter project, open the pubspec.yaml file and add the necessary Firebase dependencies:

dependencies:
flutter:
sdk: flutter
firebase_core: ^1.12.0
firebase_messaging: ^11.1.0

After adding the dependencies, run flutter pub get to fetch them.

Step 3: Initialize Firebase

Open your main.dart file and initialize Firebase in the main function:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

Step 4: Request Notification Permissions

To receive push notifications, you need to request user permission. Add the following code to your main widget (usually MyApp):

import 'package:firebase_messaging/firebase_messaging.dart';

class MyApp extends StatelessWidget {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;

@override
Widget build(BuildContext context) {
// Request notification permissions
_firebaseMessaging.requestPermission();

return MaterialApp(
// ...
);
}
}

Step 5: Handle Notifications

Now let's handle incoming notifications. Add the following code to the same widget where you requested permissions:

class MyApp extends StatelessWidget {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;

@override
void initState() {
super.initState();

// Handle incoming messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Handle the message
print("Received message: ${message.notification?.title}");
});
}

@override
Widget build(BuildContext context) {
// ...
}
}

Step 6: Displaying Notifications

To display notifications when the app is in the background or terminated, you need to set up a background message handler. Add the following code to your main widget:

class MyApp extends StatelessWidget {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;

@override
void initState() {
super.initState();

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print("Received message: ${message.notification?.title}");
});

// Handle messages when the app is in the background or terminated
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}

// Define the background message handler
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print("Handling a background message: ${message.notification?.title}");
}

@override
Widget build(BuildContext context) {
// ...
}
}

Step 7: Sending Test Notifications

Now that your Flutter app is set up to receive notifications, let's test it by sending a test notification from the Firebase Console:

  • Go to the Firebase Console and select your project.

  • Navigate to the "Cloud Messaging" tab.

  • Click on the "New Notification" button.

  • Enter the notification details and target your app.

  • Click "Send Test Message."

Conclusion

Congratulations! You've successfully integrated push notifications in your Flutter app using Firebase Cloud Messaging. You've learned how to request notification permissions, handle incoming messages, and set up background message handling. This capability opens up a world of possibilities for engaging your users and providing timely updates.

Firebase Cloud Messaging provides even more features, such as sending notifications to specific topics, customizing notification appearance, and handling user interactions with notifications. Explore the Firebase Cloud Messaging documentation to learn more about these advanced features and take your app's notification experience to the next level.

Happy coding!

ACCESSIBILITY GUIDELINES FOR FLUTTER MOBILE APPS

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

In today's digital age, mobile apps play a significant role in our lives. However, many app developers often overlook the importance of accessibility. Building mobile apps with accessibility in mind ensures that everyone, including individuals with disabilities, can access and enjoy your app without barriers. Flutter, a popular cross-platform framework, offers several features and tools to create accessible mobile apps.

In this blog, we will explore some essential accessibility guidelines for developing mobile apps with Flutter and provide example code to demonstrate each guideline.

1. Provide Meaningful Semantics

To make your app more accessible, it's crucial to use proper semantics for widgets and elements. Semantics help screen readers understand the purpose and function of each UI component.

Example: Suppose you have a custom button in your app. Use the Semantics widget to provide meaningful semantics.

Semantics(
label: 'Submit Button',
child: ElevatedButton(
onPressed: () {
// Button click logic
},
child: Text('Submit'),
),
)

2. Use Descriptive Alt Text for Images

Images are a vital part of mobile apps, but they must be accessible to users who cannot see them. Providing descriptive alternative text (alt text) for images is essential for screen readers to convey the image's content.

Example: When using an image in your app, add an Image widget with the semanticLabel parameter:

Image(
image: AssetImage('assets/image.png'),
semanticLabel: 'A beautiful sunset at the beach',
)

3. Ensure Sufficient Contrast

Maintaining proper contrast between text and background is crucial for users with visual impairments. Flutter provides a ThemeData class that allows you to define consistent colors throughout your app and adhere to accessibility standards.

Example: Define a custom theme with sufficient contrast:

ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
accentColor: Colors.orange,
textTheme: TextTheme(
bodyText1: TextStyle(color: Colors.black87),
bodyText2: TextStyle(color: Colors.black54),
),
)

4. Enable built-in Screen Reader Support in Flutter

Flutter has built-in support for screen readers like TalkBack (Android) and VoiceOver (iOS). To enable screen reader support, ensure that your UI components are accessible and convey the relevant information to the users.

Example: For adding accessibility support to a text widget:

Text(
'Hello, World!',
semanticsLabel: 'Greeting',
)

5. Manage Focus and Navigation

Proper focus management is crucial for users who rely on keyboard navigation or screen readers. Ensure that focus is visible and logical when navigating through your app's elements.

Example: Implement a FocusNode and Focus widget to manage focus:

class FocusDemo extends StatefulWidget {
@override
_FocusDemoState createState() => _FocusDemoState();
}

class _FocusDemoState extends State<FocusDemo> {
final FocusNode _focusNode = FocusNode();

@override
Widget build(BuildContext context) {
return Focus(
focusNode: _focusNode,
child: ElevatedButton(
onPressed: () {
// Button click logic
},
child: Text('Click Me'),
),
);
}
}

6. Handle Dynamic Text Sizes

Some users may rely on larger text sizes for better readability. Flutter supports dynamic text sizes that adapt to the user's accessibility settings.

Example: Use the MediaQuery to access the user's text scale factor:

dartCopy code
Text(
'Dynamic Text',
style: TextStyle(fontSize: MediaQuery.of(context).textScaleFactor * 20),
)

Conclusion

Building accessible mobile apps with Flutter is not only a legal and ethical obligation but also a step towards creating a more inclusive digital environment. By following the guidelines mentioned in this blog, you can ensure that your app is accessible to a broader audience, including individuals with disabilities.

Remember that accessibility is an ongoing process, and continuous user feedback and testing are essential to refine your app's accessibility. Let's strive to make technology more inclusive and accessible for everyone!

BASICS OF FLUTTER MODULAR

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

Flutter Modular is a package that helps you modularize your Flutter applications. It provides a way to divide your application into independent modules, each with its own set of routes, dependencies, and data. This can make your application easier to understand, maintain, and test.

In this blog we will explore the basics of Flutter Modular package and how to use it.

Why use Flutter Modular

There are many reasons why you might want to use Flutter Modular. Here are a few of the most common reasons:

  • To improve the readability and maintainability of your code. When your application is divided into modules, it becomes easier to understand how each part of the application works. This can make it easier to find and fix bugs, and to make changes to the application without breaking other parts of the code.

  • To improve the testability of your application. Modularization can make it easier to write unit tests for your application. This is because each module can be tested independently of the other modules.

  • To improve the scalability of your application. As your application grows in size and complexity, modularization can help you to keep it manageable. This is because each module can be developed and maintained by a separate team of developers.

How to use Flutter Modular

To use Flutter Modular, you first need to install the package. You can do this by running the following command in your terminal:

flutter pub add flutter_modular

Once the package is installed, you can start creating your modules. Each module should have its own directory, which contains the following files:

  • module.dart: This file defines the module's name, routes, and dependencies.

  • main.dart: This file is the entry point for the module. It typically imports the module's routes and dependencies, and then creates an instance of the module's Module class.

  • routes.dart: This file defines the module's routes. Each route is a function that returns a Widget.

  • dependencies.dart: This file defines the module's dependencies. Each dependency is a class that is needed by the module.

Once you have created your modules, you can start using them in your application. To do this, you need to import the module's module.dart file. You can then use the module's routes and dependencies in your application's code.

For example, here is a basic module.dart file for a module named home:

import 'package:flutter_modular/flutter_modular.dart';

@module
abstract class HomeModule {
@route("")
Widget homePage();
}

This module defines a single route, /, which returns a Widget named homePage().

Here is an example of the main.dart file for the same module:

import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';

import 'routes.dart';

void main() {
runApp(ModularApp(
module: HomeModule(),
));
}

This file imports the module's routes.dart file, and then creates an instance of the module's Module class.

Finally, here is an example of the routes.dart file for the same module:

import 'package:flutter_modular/flutter_modular.dart';

@moduleRoute("/")
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text("Hello, world!"),
);
}
}

This file defines the module's homePage() route, which returns a Widget that displays the text "Hello, world!".

Once you have created your modules, you can start using them in your application. To do this, you need to import the module's module.dart file. You can then use the module's routes and dependencies in your application's code.

For example, here is how you would use the homePage() route from the home module in your application's main home.dart file:

import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';

import 'home_module/module.dart';

void main() {
runApp(ModularApp(
module: HomeModule(),
child: MyApp(),
));
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My App"),
),
body: Center(
child: RaisedButton(
child: Text("Go to home page"),
onPressed: () {
Modular.to.pushNamed("/home");
},
),
),
);
}
}

This code imports the home_module/module.dart file, and then uses the Modular.to.pushNamed("/home") method to navigate to the home module's homePage() route.

Tips for using Flutter Modular

  • Use a consistent naming convention for your modules. This will make it easier to find and understand your code.

  • Use a separate module for each logical part of your application. This will help you to keep your code organized and maintainable.

  • Use dependency injection to share dependencies between modules. This will help you to decouple your modules and make them easier to test.

  • Use unit tests to test your modules independently of each other. This will help you to find and fix bugs early in the development process.

  • Use continuous integration and continuous delivery (CI/CD) to automate the deployment of your modules to production. This will help you to get your changes to production faster and more reliably.

Conclusion

Flutter Modular is a powerful tool that can help you to modularize your Flutter applications. By dividing your application into modules, you can improve the readability, maintainability, testability, and scalability of your code. If you are working on a large or complex Flutter application, then I highly recommend using Flutter Modular.

Happy coding!

GUIDE TO INTEGRATE AND USE AWS AMPLIFY AND AWS APPSYNC WITH FLUTTER MOBILE APPS

Published: · Last updated: · 7 min read
Appxiom Team
Mobile App Performance Experts

Flutter is a cross-platform mobile development framework that allows you to build native apps for iOS and Android from a single codebase. AWS Amplify is a set of tools and services that make it easy to build and deploy cloud-powered mobile apps. It also supports local persistence with automatic sync with cloud data store.

In this blog post, we will show you how to build a CRUD Flutter mobile app using AWS Amplify and AWS AppSync. We will create a simple app that allows users to create, read, update, and delete trips.

Prerequisites

To follow this blog post, you will need the following:

  • A Flutter development environment

  • An AWS account

  • The AWS Amplify CLI

Step 1: Create a new Flutter project

First, we need to create a new Flutter project. We can do this by running the following command in the terminal:

flutter create amplify_crud_app

This will create a new Flutter project called amplify_crud_app.

Step 2: Initialize AWS Amplify

Next, we need to initialize AWS Amplify in our Flutter project. We can do this by running the following command in the terminal:

amplify init

The amplify init command will initialize AWS Amplify in your Flutter project. This command will create a new file called amplifyconfiguration.json in the root directory of your project. This file will contain the configuration settings for your AWS Amplify project.

When you run the amplify init command, you will be prompted to answer a few questions about your project. These questions include:

  • The name of your project

  • The region that you want to deploy your project to

  • The environment that you want to create (e.g., dev, staging, prod)

  • The type of backend that you want to use (e.g., AWS AppSync, AWS Lambda)

Once you have answered these questions, the amplify init command will create the necessary resources in AWS.

Step 3: Configure AWS Amplify

Once you have initialized AWS Amplify, you need to configure it. You can do this by running the following command in the terminal:

amplify configure

This command will open a wizard that will guide you through the process of configuring AWS Amplify.

When you run the amplify configure command, you will be prompted to enter your AWS credentials. You can also choose to configure other settings, such as the name of your app, the region that you want to deploy your app to, and the environment that you want to use.

Step 4: Creating a GraphQL API

The amplify add api command will create a GraphQL API in AWS AppSync. This GraphQL API will allow us to interact with the data in our Trip data model.

The amplify add api command will prompt you to enter a few details about the GraphQL API that you want to create. These details include:

  • The name of the GraphQL API

  • The schema for the GraphQL API

  • The authentication method for the GraphQL API

Once you have entered these details, the amplify add api command will create the GraphQL API in AWS AppSync.

The Trip schema

The Trip schema will define the structure of the data that we can query and mutate in our GraphQL API. The Trip schema will include the following fields:

  • id: The ID of the trip. This field will be a unique identifier for the trip.

  • name: The name of the trip.

  • destination: The destination of the trip.

  • startDateTime: The start date and time of the trip.

  • endDateTime: The end date and time of the trip.

These are just a few examples of the fields that you could include in your Trip schema. You can customize the schema to meet the specific needs of your application.

Authentication

The amplify add api command will also prompt you to choose an authentication method for your GraphQL API. You can choose to use Amazon Cognito or AWS IAM for authentication.

If you choose to use Amazon Cognito, you will need to create a user pool and a user pool client. You can do this by using the AWS Management Console or the AWS CLI.

Once you have created a user pool and a user pool client, you can configure your GraphQL API to use Amazon Cognito for authentication.

Step 5: Creating a data model

We need to create a data model for our CRUD Flutter mobile app. This data model will define the structure of the data that we will store in AWS AppSync.

To create a data model, we need to run the following command in the terminal:

amplify add api --model Trip

This will create a data model called Trip.

The amplify add api --model Trip command will create a data model called Trip in AWS AppSync. This data model will define the structure of the data that we will store in AWS AppSync.

The amplify add api --model command will prompt you to enter a few details about the data model that you want to create. These details include:

  • The name of the data model

  • The fields that you want to include in the data model

  • The types of the fields

Once you have entered these details, the amplify add api --model command will create the data model in AWS AppSync.

The Trip data model

The Trip data model that we will create in this blog post will have the following fields:

  • id: The ID of the trip. This field will be a unique identifier for the trip.

  • name: The name of the trip.

  • destination: The destination of the trip.

  • startDateTime: The start date and time of the trip.

  • endDateTime: The end date and time of the trip.

These are just a few examples of the fields that you could include in your Trip data model. You can customize the fields in your data model to meet the specific needs of your application.

Step 6: Implementing the CRUD operations

Once we have created the data model and the GraphQL API, we need to implement the CRUD operations for our CRUD Flutter mobile app. This means that we need to implement code to create, read, update, and delete trips.

We can implement the CRUD operations by using the amplify-flutter library. This library provides us with a set of widgets that we can use to interact with AWS AppSync. The data will be persisted locally first, and if the internet connectivity is available it will sync with cloud.

The amplify-flutter library includes a widget called AmplifyDataStore. This widget allows us to interact with the data in our Trip data model.

Here is an example:

To create a trip, we can use the Amplify.DataStore.save() method provided by amplify_flutter. Let's take a look at the code snippet below:

final trip = Trip(
name: 'My Trip',
destination: 'London',
startDateTime: DateTime.now(),
endDateTime: DateTime.now().add(Duration(days: 7)),
);

try {
await Amplify.DataStore.save(trip);
print('Trip created successfully');
} catch (e) {
print('Error creating trip: $e');
}

To read a specific trip from the data store, we can utilize the Amplify.DataStore.query() method. Let's see how it's done:

final tripId = '1234567890';

try {
final trip = await Amplify.DataStore.query(Trip.classType, where: {
'id': tripId,
});

print('Trip: ${trip.name}');
} catch (e) {
print('Error reading trip: $e');
}

To update a trip, we need to retrieve it from the data store, modify its properties, and save it back using the Amplify.DataStore.save() method. Here's an example:

final tripId = '1234567890';
final newName = 'My New Trip';

try {
final trip = await Amplify.DataStore.query(Trip.classType, where: {
'id': tripId,
});

trip.name = newName;

await Amplify.DataStore.save(trip);
print('Trip updated successfully');
} catch (e) {
print('Error updating trip: $e');
}

To delete a trip from the data store, we can use the Amplify.DataStore.delete() method. Here's an example:

final tripId = '1234567890';

try {
await Amplify.DataStore.delete(Trip.classType, where: {
'id': tripId,
});
print('Trip deleted successfully');
} catch (e) {
print('Error deleting trip: $e');
}

Step 6: Run the app

Once we have implemented the CRUD operations, we can run the app. To do this, we can run the following command in the terminal:

flutter run

This will run the app in the emulator or on a physical device.

Conclusion

In this blog post, we showed you how to build a CRUD Flutter mobile app using AWS Amplify. We created a simple app that allows users to create, read, update, and delete trips.

I hope you found this blog post helpful. If you have any questions, please leave a comment below.

HOW TO HARNESS THE POWER OF MEDIA APIS IN FLUTTER

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

In today's digital era, multimedia content plays a vital role in app development, enriching the user experience and providing engaging features. Flutter, the cross-platform UI toolkit, offers a wide array of media APIs that allow developers to incorporate images, videos, and audio seamlessly into their applications.

In this blog post, we will explore the basics of various media APIs provided by Flutter and demonstrate their usage with code examples.

1. Displaying Images

Displaying images is a fundamental aspect of many mobile applications. Flutter provides the Image widget, which simplifies the process of loading and rendering images.

Here's an example of loading an image from a network URL:

import 'package:flutter/material.dart';

class ImageExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Image.network(
'https://example.com/image.jpg',
fit: BoxFit.cover,
);
}
}

2. Playing Videos

To integrate video playback in your Flutter app, you can utilize the chewie and video_player packages. The chewie package wraps the video_player package, providing a customizable video player widget.

Here's an example of auto-playing a local video file:

import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';

class VideoExample extends StatefulWidget {
@override
_VideoExampleState createState() => _VideoExampleState();
}

class _VideoExampleState extends State<VideoExample> {
VideoPlayerController _videoPlayerController;
ChewieController _chewieController;

@override
void initState() {
super.initState();
_videoPlayerController = VideoPlayerController.asset('assets/video.mp4');
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: true,
);
}

@override
void dispose() {
_videoPlayerController.dispose();
_chewieController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Chewie(
controller: _chewieController,
);
}
}

3. Playing Audio

Flutter's audioplayers package provides a convenient way to play audio files in your app.

Here's an example of playing an audio file from the internet when a button is clicked:

import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';

class AudioExample extends StatefulWidget {
@override
_AudioExampleState createState() => _AudioExampleState();
}

class _AudioExampleState extends State<AudioExample> {
AudioPlayer _audioPlayer;
String _audioUrl =
'https://example.com/audio.mp3';

@override
void initState() {
super.initState();
_audioPlayer = AudioPlayer();
_audioPlayer.setUrl(_audioUrl);
}

@override
void dispose() {
_audioPlayer.stop();
_audioPlayer.release();
super.dispose();
}

@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {
_audioPlayer.play(_audioUrl);
},
);
}
}

Conclusion

In this blog post, we have explored the basic usage of powerful media APIs available in Flutter, enabling developers to incorporate rich media content into their applications effortlessly. We covered displaying images, playing videos, and playing audio using the respective Flutter packages. By leveraging these media APIs, you can create immersive and interactive experiences that captivate your users. So go ahead and unlock the potential of media in your Flutter projects!

Remember, this blog post provides a high-level overview of using media APIs with Flutter, and there are many more advanced techniques and features you can explore. The Flutter documentation and community resources are excellent sources to dive deeper into media integration in Flutter applications.

Happy coding!

TIPS AND TOOLS FOR PROFILING FLUTTER APPS

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

Flutter, the popular cross-platform framework, allows developers to build high-performance mobile applications. However, ensuring optimal performance is crucial to deliver a smooth and responsive user experience. Profiling your Flutter apps is a powerful technique that helps identify performance bottlenecks and optimize your code.

In this blog post, we will explore various profiling techniques and tools to enhance the performance of your Flutter applications.

Why Profile Flutter Apps?

Profiling is essential for understanding how your app behaves in different scenarios and identifying areas that need optimization. By profiling your Flutter app, you can:

1. Identify performance bottlenecks

Profiling helps you pinpoint specific areas of your code that may be causing performance issues, such as excessive memory usage, slow rendering, or inefficient algorithms.

2. Optimize resource consumption

By analyzing CPU usage, memory allocations, and network requests, you can optimize your app's resource utilization and minimize battery drain.

3. Enhance user experience

Profiling enables you to eliminate jank (stuttering animations) and reduce app startup time, resulting in a smoother and more responsive user interface.

Profiling Techniques

Before diving into the tools, let's discuss some essential profiling techniques for Flutter apps:

1. CPU Profiling

This technique focuses on measuring the CPU usage of your app. It helps identify performance bottlenecks caused by excessive computations or poorly optimized algorithms.

2. Memory Profiling

Memory usage is critical for app performance. Memory profiling helps you identify memory leaks, unnecessary allocations, or excessive memory usage that can lead to app crashes or sluggish behavior.

3. Network Profiling

Network requests play a significant role in app performance. Profiling network activity helps identify slow or excessive requests, inefficient data transfers, or potential bottlenecks in the network stack.

4. Frame Rendering Profiling

Flutter's UI is rendered in frames. Profiling frame rendering helps detect jank and optimize UI performance by analyzing the time taken to render each frame and identifying potential rendering issues.

Profiling Tools for Flutter

Flutter provides a range of profiling tools and libraries to assist developers in optimizing their applications. Let's explore some of the most useful tools:

1. Flutter DevTools

Flutter DevTools is an official tool provided by the Flutter team. It offers a comprehensive set of profiling and debugging features. With DevTools, you can analyze CPU, memory, and frame rendering performance, inspect widget trees, and trace specific code paths to identify performance bottlenecks.

2. Observatory

Observatory is another powerful profiling tool included with the Flutter SDK. It provides insights into memory usage, CPU profiling, and Dart VM analytics. It allows you to monitor and analyze the behavior of your app in real-time, making it useful for identifying performance issues during development.

3. Dart Observatory Timeline

The Dart Observatory Timeline provides a graphical representation of the execution of Dart code. It allows you to analyze the timing of method calls, CPU usage, and asynchronous operations. This tool is particularly useful for identifying slow or inefficient code paths.

4. Android Profiler and Xcode Instruments

If you are targeting specific platforms like Android or iOS, you can leverage the native profiling tools provided by Android Profiler and Xcode Instruments. These tools offer advanced profiling capabilities, including CPU, memory, and network analysis, tailored specifically for the respective platforms.

5. Performance Monitoring Tools

Even after extensive testing and analyzing you cannot rule out the possibility of issues in the app. That is where continuous app performance monitoring tools like BugSnag, AppDynamics, Appxiom and Dynatrace become relevant. These tools will generate issue reports in realtime and developer will be able to reproduce and fix the issues in apps.

Profiling Best Practices

To make the most of your profiling efforts, consider the following best practices:

1. Replicate real-world scenarios

Profile your app using realistic data and scenarios that resemble the expected usage patterns. This will help you identify performance issues that users might encounter in practice.

2. Profile on different devices

Test your app on various devices with different hardware configurations and screen sizes. This allows you to uncover device-specific performance issues and ensure a consistent experience across platforms.

3. Profile across different app states

Profile your app in different states, such as cold startup, warm startup, heavy data load, or low memory conditions. This will help you understand how your app behaves in various scenarios and optimize performance accordingly.

4. Optimize critical code paths

Focus on optimizing the critical code paths that contribute significantly to the overall app performance. Use profiling data to identify areas that require improvement and apply performance optimization techniques like caching, lazy loading, or algorithmic enhancements.

Conclusion

Profiling Flutter apps is an integral part of the development process to ensure optimal performance and a delightful user experience. By utilizing the profiling techniques discussed in this blog and leveraging the available tools, you can identify and resolve performance bottlenecks, optimize resource consumption, and enhance the overall performance of your Flutter applications. Embrace the power of profiling to deliver high-performing apps that leave a lasting impression on your users.

INTEGRATING AND USING ML KIT WITH FLUTTER

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

Google ML Kit is a powerful set of Flutter plugins that allows developers to incorporate machine learning capabilities into their Flutter apps. With ML Kit, you can leverage various machine learning features, such as text recognition, face detection, image labeling, landmark recognition, and barcode scanning.

In this blog post, we will guide you through the process of integrating and using ML Kit with Flutter. We'll demonstrate the integration by building a simple app that utilizes ML Kit to recognize text in an image.

Prerequisites

Before we get started, make sure you have the following:

  • A Flutter development environment set up

  • Basic understanding of Flutter framework

  • A Google Firebase project (ML Kit relies on Firebase for certain functionalities)

Now, let's dive into the steps for integrating and using ML Kit with Flutter.

Step 1: Add the dependencies

To begin, we need to add the necessary ML Kit dependencies to our Flutter project. Open the pubspec.yaml file in your project and include the following lines:

dependencies:google_ml_kit: ^4.0.0

Save the file and run flutter pub get to fetch the required dependencies.

Step 2: Initialize ML Kit

To use ML Kit in your Flutter app, you need to initialize it first. This initialization process is typically done in the main() function of your app. Open the main.dart file and modify the code as follows:

void main() {
WidgetsFlutterBinding.ensureInitialized();
initMLKit();
runApp(MyApp());
}

The initMLKit() function is a custom function that we'll define shortly. It handles the initialization of ML Kit. The WidgetsFlutterBinding.ensureInitialized() line ensures that Flutter is initialized before ML Kit is initialized.

Step 3: Create a text recognizer

Now, let's create a text recognizer object. The text recognizer is responsible for detecting and recognizing text in an image. Add the following code snippet to the main.dart file:

TextRecognizer recognizer = TextRecognizer.instance();

The TextRecognizer.instance() method creates an instance of the text recognizer.

Step 4: Recognize text in an image

With the text recognizer created, we can now use it to recognize text in an image. To achieve this, call the recognizeText() method on the recognizer object and pass the image as a parameter. Update the code as shown below:

List<TextBlock> textBlocks = recognizer.recognizeText(image);

Here, image represents the image on which you want to perform text recognition. The recognizeText() method processes the image and returns a list of TextBlock objects. Each TextBlock represents a distinct block of recognized text.

Step 5: Display the recognized text

Finally, let's display the recognized text in our app. For the sake of simplicity, we'll print the recognized text to the console. Replace the placeholder code with the following snippet:

for (TextBlock textBlock in textBlocks) {
print(textBlock.text);
}

This loop iterates through each TextBlock in the textBlocks list and prints its content to the console.

Complete code

Now that we've covered all the necessary steps, let's take a look at the complete code for our Flutter app:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_ml_kit/google_ml_kit.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
initMLKit();
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ML Kit Text Recognition',
home: Scaffold(
appBar: AppBar(
title: Text('ML Kit Text Recognition'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 200,
width: 200,
child: Image.asset('assets/image.jpg'),
),
Text('Recognized text:'),
Text('(Will be displayed here)')
],
),
),
),
);
}
}

void initMLKit() async {
await TextRecognizer.instance().initialize();
}

This code defines a basic Flutter app with a simple UI. When the app runs, it displays an image and a placeholder for the recognized text.

Running the app

To run the app, you can build and run it from your preferred Flutter development environment. Once the app is running, tap on the image to initiate text recognition. The recognized text will be printed to the console.

Conclusion

Congratulations! In this blog post, we walked you through the process of integrating and using ML Kit with Flutter. We built a simple app that utilizes ML Kit to recognize text in an image. You can use this tutorial as a starting point to develop your own ML Kit-powered apps.

For more in-depth information on ML Kit and its capabilities, please refer to the official ML Kit documentation: https://developers.google.com/ml-kit/.

Feel free to experiment with different ML Kit features and explore its vast potential in your Flutter apps.

Happy coding!

GUIDE FOR INTEGRATING GRAPHQL WITH FLUTTER USING HASURA

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

In today's mobile app development landscape, building data-driven applications is a common requirement. To efficiently handle data fetching and manipulation, it's crucial to have a robust API layer that simplifies the communication between the frontend and backend.

GraphQL, a query language for APIs, and Hasura, an open-source GraphQL engine, offer a powerful combination for building data-driven Flutter apps. In this blog post, we will explore how to integrate GraphQL with Flutter using Hasura and leverage its features to create efficient and scalable apps.

Prerequisites

To follow along with this tutorial, you should have the following prerequisites:

  • Basic knowledge of Flutter and Dart.

  • Flutter SDK installed on your machine.

  • An existing Flutter project or create a new one using flutter create my_flutter_app.

Set up Hasura GraphQL Engine

Before integrating GraphQL with Flutter, we need to set up the Hasura GraphQL Engine to expose our data through a GraphQL API. Here's a high-level overview of the setup process:

1. Install Hasura GraphQL Engine:

  • Option 1: Using Docker:

Install Docker on your machine if you haven't already.

  • Pull the Hasura GraphQL Engine Docker image using the command: docker pull hasura/graphql-engine.

  • Start the Hasura GraphQL Engine container: docker run -d -p 8080:8080 hasura/graphql-engine.

  • Option 2: Using Hasura Cloud:

Visit the Hasura Cloud website (https://hasura.io/cloud) and sign up for an account.

  • Create a new project and follow the setup instructions provided.

2. Set up Hasura Console

  • Access the Hasura Console by visiting http://localhost:8080 or your Hasura Cloud project URL.

  • Authenticate with the provided credentials (default is admin:admin).

  • Create a new table or use an existing one to define your data schema.

3. Define GraphQL Schema

Use the Hasura Console to define your GraphQL schema by auto-generating it from an existing database schema or manually defining it using the GraphQL SDL (Schema Definition Language).

4. Explore GraphQL API

Once the schema is defined, you can explore the GraphQL API by executing queries, mutations, and subscriptions in the Hasura Console.

Congratulations! You have successfully set up the Hasura GraphQL Engine. Now, let's integrate it into our Flutter app.

Add Dependencies

To use GraphQL in Flutter, we need to add the necessary dependencies to our pubspec.yaml file. Open the file and add the following lines:

dependencies:flutter:sdk: fluttergraphql_flutter: ^5.1.2

Save the file and run flutter pub get to fetch the dependencies.

Create GraphQL Client

To interact with the Hasura GraphQL API, we need to create a GraphQL client in our Flutter app. Create a new file, graphql_client.dart, and add the following code:

import 'package:graphql_flutter/graphql_flutter.dart';

class GraphQLService {
static final HttpLink httpLink = HttpLink('http://localhost:8080/v1/graphql');

static final GraphQLClient client = GraphQLClient(
link: httpLink,
cache: GraphQLCache(),
);
}

In the above code, we define an HTTP link to connect to our Hasura GraphQL API endpoint. You may need to update the URL if you are using Hasura Cloud or a different port. We then create a GraphQL client using the GraphQLClient class from the graphql_flutter package.

Query Data from Hasura

Now, let's fetch data from the Hasura GraphQL API using our GraphQL client. Update your main Flutter widget (main.dart) with the following code:

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';

import 'graphql_client.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GraphQLProvider(
client: GraphQLService.client,
child: MaterialApp(
title: 'Flutter GraphQL Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GraphQL Demo'),
),
body: Query(
options: QueryOptions(
document: gql('YOUR_GRAPHQL_QUERY_HERE'),
),
builder: (QueryResult result, {VoidCallback? refetch}) {
if (result.hasException) {
return Text(result.exception.toString());
}

if (result.isLoading) {
return CircularProgressIndicator();
}

// Process the result.data object and display the data in your UI
// ...

return Container();
},
),
);
}
}

In the above code, we wrap our Flutter app with the GraphQLProvider widget, which provides the GraphQL client to all descendant widgets. Inside the MyHomePage widget, we use the Query widget from graphql_flutter to execute a GraphQL query. Replace 'YOUR_GRAPHQL_QUERY_HERE' with the actual GraphQL query you want to execute.

Display Data in the UI

Inside the builder method of the Query widget, we can access the query result using the result parameter. Process the result.data object to extract the required data and display it in your UI. You can use any Flutter widget to display the data, such as Text, ListView, or custom widgets.

Congratulations! You have successfully integrated GraphQL with Flutter using Hasura. You can now fetch and display data from your Hasura GraphQL API in your Flutter app.

Conclusion

In this blog post, we explored how to integrate GraphQL with Flutter using Hasura. We set up the Hasura GraphQL Engine, created a GraphQL client in Flutter, queried data from the Hasura GraphQL API, and displayed it in the UI.

By leveraging the power of GraphQL and the simplicity of Hasura, you can build efficient and scalable data-driven apps with Flutter.

Remember to handle error scenarios, mutations, and subscriptions based on your app requirements. Explore the graphql_flutter package documentation for more advanced usage and features.

Happy coding!

USING FLUTTER_NATIVE_IMAGE PLUGIN TO DO IMAGE PROCESSING IN FLUTTER APPS

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Image processing plays a crucial role in many mobile applications, enabling developers to enhance, manipulate, and optimize images according to specific requirements. Flutter, a cross-platform framework, provides numerous tools and packages to handle image processing tasks effectively.

In this blog post, we will explore the flutter_native_image package, which offers advanced image processing capabilities in Flutter applications.

What is flutter_native_image?

flutter_native_image is a powerful Flutter package that allows developers to perform image processing operations using native code. It leverages the native image processing capabilities available on both Android and iOS platforms, resulting in faster and more efficient image operations.

Installation

To begin using flutter_native_image in your Flutter project, add it as a dependency in your pubspec.yaml file:

dependencies:flutter_native_image: ^1.0.6

After adding the dependency, run flutter pub get to fetch the package and its dependencies.

Using flutter_native_image

The flutter_native_image package provides various image processing operations, including resizing, cropping, rotating, compressing, and more. Let's explore some of these operations with code samples.

1. Resizing Images

Resizing images is a common requirement in mobile applications. The flutter_native_image package makes it straightforward to resize images in Flutter.

Here's an example of resizing an image to a specific width and height:

import 'package:flutter_native_image/flutter_native_image.dart';

Future<void> resizeImage() async {
String imagePath = 'path/to/image.jpg';
ImageProperties properties = await FlutterNativeImage.getImageProperties(imagePath);
File resizedImage = await FlutterNativeImage.resizeImage(
imagePath: imagePath,
targetWidth: 500,
targetHeight: 500,
);
// Process the resized image further or display it in your Flutter UI.
}

2. Compressing Images

Image compression is essential to reduce the file size of images without significant loss of quality. The flutter_native_image package allows you to compress images efficiently.

Here's an example:

import 'package:flutter_native_image/flutter_native_image.dart';

Future<void> compressImage() async {
String imagePath = 'path/to/image.jpg';
File compressedImage = await FlutterNativeImage.compressImage(
imagePath,
quality: 80,
percentage: 70,
);
// Process the compressed image further or display it in your Flutter UI.
}

3. Rotating Images

In some cases, you may need to rotate images based on user interactions or other requirements. The flutter_native_image package simplifies image rotation tasks.

Here's an example:

import 'package:flutter_native_image/flutter_native_image.dart';

Future<void> rotateImage() async {
String imagePath = 'path/to/image.jpg';
File rotatedImage = await FlutterNativeImage.rotateImage(
imagePath: imagePath,
degree: 90,
);
// Process the rotated image further or display it in your Flutter UI.
}

4. Cropping Images

Cropping images allows you to extract specific regions of interest from an image. The flutter_native_image package enables easy cropping of images. Here's an example:

import 'package:flutter_native_image/flutter_native_image.dart';

Future<void> cropImage() async {
String imagePath = 'path/to/image.jpg';
File croppedImage = await FlutterNativeImage.cropImage(
imagePath: imagePath,
originX: 100,
originY: 100,
width: 300,
height: 300,
);
// Process the cropped image further or display it in your Flutter UI.
}

Conclusion

Image processing is a fundamental aspect of many Flutter applications, and the flutter_native_image package simplifies the process by leveraging the native image processing capabilities of Android and iOS platforms.

In this blog post, we explored some of the key image processing operations, including resizing, compressing, rotating, and cropping images using flutter_native_image. By incorporating these operations into your Flutter project, you can enhance the visual experience, optimize image sizes, and meet specific application requirements efficiently.

Remember to check the official flutter_native_image package documentation for more information and additional functionalities.

Happy coding!

MEDIAQUERY AS AN INHERITEDMODEL IN FLUTTER 3.10

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

In Flutter 3.10, an exciting change was introduced to the way MediaQuery is handled. MediaQuery, which provides access to the media information of the current context, was transformed into an InheritedModel. This change simplifies the process of accessing MediaQueryData throughout your Flutter application.

In this blog post, we will explore the implications of this change and how it affects the way we work with MediaQuery in Flutter.

Understanding InheritedModel

Before diving into the specifics of how MediaQuery became an InheritedModel, let's briefly understand what InheritedModel is in Flutter. InheritedModel is a Flutter widget that allows the propagation of data down the widget tree. It provides a way to share data with descendant widgets without having to pass it explicitly through constructors.

In previous versions of Flutter, MediaQuery was not an InheritedModel, meaning that accessing MediaQueryData in nested widgets required some extra steps. However, starting from Flutter 3.10, MediaQuery became an InheritedModel, streamlining the process of accessing and using media-related information across your app.

Simplified Access to MediaQueryData

With the migration of MediaQuery to an InheritedModel, accessing MediaQueryData became much simpler. Previously, you needed to use a StatefulWidget and a GlobalKey to store and retrieve MediaQueryData. However, after Flutter 3.10, you can directly use the MediaQuery.of(context) method to access the MediaQueryData for the current context.

The new approach allows you to obtain MediaQueryData anywhere in your widget tree without the need for additional boilerplate code. Simply provide the appropriate context, and you will have access to valuable information such as the size, orientation, and device pixel ratio.

Benefits of InheritedModel

The shift of MediaQuery to an InheritedModel offers several benefits for Flutter developers:

  • Simplified Code: The direct usage of MediaQuery.of(context) eliminates the need for GlobalKey and StatefulWidget, resulting in cleaner and more concise code.

  • Improved Performance: As an InheritedModel, MediaQuery optimizes the propagation of changes to MediaQueryData throughout the widget tree. This means that only the necessary widgets will be rebuilt when media-related information changes, resulting in improved performance.

  • Enhanced Flexibility: By leveraging the InheritedModel approach, you can easily access MediaQueryData from any descendant widget within your app's widget tree. This flexibility enables you to respond dynamically to changes in the device's media attributes and adapt your UI accordingly.

Accessing MediaQueryData Before Flutter 3.10

Before Flutter 3.10, accessing MediaQueryData required the use of a StatefulWidget and GlobalKey.

Let's take a look at the code example:

import 'package:flutter/material.dart';

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
final GlobalKey<_MyAppState> _key = GlobalKey();
MediaQueryData _mediaQueryData;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_mediaQueryData = MediaQuery.of(_key.currentContext);
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text(
_mediaQueryData.size.toString(),
),
),
),
);
}
}

In the code snippet above, we define a StatefulWidget, MyApp, which holds a GlobalKey and the MediaQueryData object. Inside the initState method, we access the MediaQuery.of(_key.currentContext) to obtain the MediaQueryData. Finally, in the build method, we display the size of the device screen using the obtained MediaQueryData.

Accessing MediaQueryData in Flutter 3.10

With the introduction of InheritedModel in Flutter 3.10, accessing MediaQueryData became much simpler.

Let's take a look at the updated code example:

import 'package:flutter/material.dart';

void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Builder(
builder: (context) {
final mediaQueryData = MediaQuery.of(context);
return Text(
mediaQueryData.size.toString(),
);
},
),
),
),
),
);
}

In the updated code, we can now directly use MediaQuery.of(context) to access the MediaQueryData within any widget. We use the Builder widget to provide a new BuildContext where we can access the MediaQueryData. Inside the builder function, we obtain the mediaQueryData using MediaQuery.of(context) and display the size of the device screen using a Text widget.

Conclusion

Flutter 3.10 introduced a significant change to the way we access MediaQueryData by transforming MediaQuery into an InheritedModel. This change simplifies the code and eliminates the need for StatefulWidget and GlobalKey to access MediaQueryData. By leveraging the power of InheritedModel, accessing MediaQueryData becomes a straightforward process using MediaQuery.of(context).

As a Flutter developer, staying up-to-date with the latest changes in the framework is crucial. Understanding the migration from StatefulWidget and GlobalKey to InheritedModel ensures that you can write more concise and efficient code. By embracing the simplified approach to accessing MediaQueryData, you can create responsive and adaptable user interfaces in your Flutter applications.

USING METHOD CHANNELS TO ENABLE CALLS BETWEEN NATIVE CODE AND FLUTTER CODE

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

Flutter, a popular cross-platform development framework, allows developers to build high-performance applications with a single codebase. However, there are times when you need to integrate platform-specific functionality into your Flutter app. Method Channels provide a powerful mechanism to bridge the gap between Flutter and native code, enabling you to call native methods from Flutter and vice versa.

In this blog, we'll explore how to utilize Method Channels to invoke native code in both Android and iOS platforms from your Flutter app.

Prerequisites

To follow along with this tutorial, you should have a basic understanding of Flutter and have Flutter SDK installed on your machine.

Additionally, make sure you have the necessary tools and configurations set up for Android and iOS development, such as Android Studio and Xcode.

Implementing Method Channels in Flutter

Step 1: Create a Flutter Project Let's start by creating a new Flutter project. Open your terminal or command prompt and run the following command:

flutter create method_channel_demo
cd method_channel_demo

Step 2: Add Dependencies Open the pubspec.yaml file in your project's root directory and add the following dependencies:

dependencies:flutter:sdk: flutter
dev_dependencies:flutter_test:sdk: flutter

Save the file and run flutter pub get in your terminal to fetch the dependencies.

Step 3: Define the Native Method Channel Create a new Dart file named method_channel.dart in the lib directory. In this file, define a class called MethodChannelDemo that will encapsulate the native method channel communication. Add the following code:

import 'package:flutter/services.dart';

class MethodChannelDemo {
static const platform = MethodChannel('method_channel_demo');

static Future<String> getPlatformVersion() async {
return await platform.invokeMethod('getPlatformVersion');
}
}

In this code, we define a static platform object of type MethodChannel and associate it with the channel name 'method_channel_demo'. We also define a getPlatformVersion() method that invokes the native method 'getPlatformVersion' using the invokeMethod() function.

Step 4: Implement Native Code Next, let's implement the native code for both Android and iOS platforms.

For Android, open the MainActivity.kt file and import the necessary packages:

import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel

Inside the MainActivity class, override the configureFlutterEngine() method and register the method channel:

class MainActivity : FlutterActivity() {
private val CHANNEL = "method_channel_demo"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
if (call.method == "getPlatformVersion") {
result.success("Android ${VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
}
}

The code above sets up a method channel with the same name as defined in the Dart code. It handles the method call with a lambda function where we check the method name and return the Android platform version using the result.success() method.

For iOS, open the AppDelegate.swift file and import the necessary packages:

import UIKit
import Flutter
import UIKit.UIApplication
import UIKit.UIWindow

Inside the AppDelegate class, add the following code to register the method channel:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private let CHANNEL = "method_channel_demo"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

GeneratedPluginRegistrant.register(with: self)
let controller = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: CHANNEL,
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getPlatformVersion" {
result("iOS " + UIDevice.current.systemVersion)
} else {
result(FlutterMethodNotImplemented)
}
})

return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

In this code, we create a method channel with the same name as defined in the Dart code. We handle the method call using a closure, check the method name, and return the iOS platform version using the result() method.

Step 5: Call Native Code from Flutter Now that we have set up the method channels and implemented the native code, let's invoke the native methods from Flutter.

Open the lib/main.dart file and replace its contents with the following code:

import 'package:flutter/material.dart';
import 'method_channel.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Method Channel Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<String>(
future: MethodChannelDemo.getPlatformVersion(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Platform version: ${snapshot.data}');
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return CircularProgressIndicator();
},
),
],
),
),
),
);
}
}

In this code, we import the method_channel.dart file and create a simple Flutter app with a centered column containing a FutureBuilder. The FutureBuilder calls the getPlatformVersion() method and displays the platform version once it's available.

Step 6: Run the App Finally, we're ready to run our app. Connect a physical device or start an emulator, then run the following command in your terminal:

flutter run

You have successfully implemented Method Channels to call native code in Android and iOS platforms from your Flutter app. You can now leverage this mechanism to access platform-specific APIs and extend the functionality of your Flutter applications.

Conclusion

In this tutorial, we explored how to utilize Method Channels to invoke native code in Android and iOS platforms from a Flutter app. We covered the steps required to set up the method channels, implemented the native code for Android and iOS, and demonstrated how to call native methods from Flutter. By leveraging Method Channels, Flutter developers can access platform-specific features and create powerful cross-platform applications. Happy coding!

DATA PERSISTENCE IN FLUTTER

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

In today's app development landscape, databases play a crucial role in managing and storing data. Flutter, a popular cross-platform framework, offers various options for integrating databases into your applications.

In this blog, we will explore the fundamental database concepts in Flutter and provide code examples to illustrate their implementation. So, let's dive in and learn how to effectively work with databases in Flutter!

Introduction to Databases

A database is a structured collection of data that allows efficient storage, retrieval, and manipulation of information. In the context of app development, databases are used to store and manage data persistently, enabling apps to function seamlessly even when offline or across different devices.

Local Data Persistence in Flutter

Local data persistence refers to the storage of data on the device itself. Flutter provides several libraries and techniques for local data persistence.

Some popular options include:

Shared Preferences

Shared Preferences is a simple key-value store that allows you to store primitive data types such as strings, integers, booleans, etc. It's suitable for storing small amounts of data that don't require complex querying.

import 'package:shared_preferences/shared_preferences.dart';

void saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('username', 'JohnDoe');
}

void loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String username = prefs.getString('username');
print('Username: $username');
}

Hive

Hive is a lightweight and fast NoSQL database for Flutter. It offers a simple key-value store as well as support for more complex data structures. Hive is known for its excellent performance and ease of use.

import 'package:hive/hive.dart';

void saveData() async {
var box = await Hive.openBox('myBox');
await box.put('username', 'JohnDoe');
}

void loadData() async {
var box = await Hive.openBox('myBox');
String username = box.get('username');
print('Username: $username');
}

SQLite Database Integration

SQLite is a widely used relational database management system (RDBMS) that provides a self-contained, serverless, and zero-configuration SQL database engine. Flutter offers seamless integration with SQLite, enabling you to create and manage structured databases efficiently.

Setting up SQLite in Flutter

To use SQLite in Flutter, you need to include the sqflite package in your pubspec.yaml file and import the necessary dependencies.

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> initializeDatabase() async {
String path = join(await getDatabasesPath(), 'my_database.db');
return await openDatabase(
path,
version: 1,
onCreate: (Database db, int version) async {
// Create tables and define schemas
await db.execute(
'CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)',
);
},
);
}

Performing CRUD Operations with SQLite

Once the database is initialized, you can perform various CRUD (Create, Read, Update, Delete) operations on it using SQL queries.

Future<void> insertUser(User user) async {
final db = await database;
await db.insert(
'users',
user.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}

Future<List<User>> getUsers() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query('users');
return List.generate(maps.length, (i) {
return User(
id: maps[i]['id'],
name: maps[i]['name'],
);
});
}

Working with Firebase Realtime Database

Firebase Realtime Database is a NoSQL cloud-hosted database that enables real-time data synchronization across devices. It offers seamless integration with Flutter, allowing you to store and sync structured data easily.

Setting up Firebase Realtime Database

To use Firebase Realtime Database in Flutter, you need to create a Firebase project, add the necessary dependencies in your pubspec.yaml file, and configure Firebase in your Flutter app.

Performing CRUD Operations with Firebase Realtime Database

Firebase Realtime Database uses a JSON-like structure to store and organize data. You can perform CRUD operations using the Firebase SDK.

import 'package:firebase_database/firebase_database.dart';

void insertUser(User user) {
DatabaseReference usersRef =
FirebaseDatabase.instance.reference().child('users');
usersRef.push().set(user.toJson());
}

void getUsers() {
DatabaseReference usersRef =
FirebaseDatabase.instance.reference().child('users');
usersRef.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key, values) {
print('ID: $key');
print('Name: ${values['name']}');
});
});
}

Implementing GraphQL with Hasura and Flutter

GraphQL is a query language for APIs that provides a flexible and efficient approach to data fetching. Hasura is an open-source engine that provides instant GraphQL APIs over databases. By combining Flutter, Hasura, and GraphQL, you can create powerful and responsive apps with real-time data capabilities.

Setting up Hasura and GraphQL in Flutter

To integrate Hasura and GraphQL into your Flutter app, you need to set up a Hasura server and define your database schema. Then, use the graphql package in Flutter to interact with the GraphQL API.

Performing GraphQL Operations with Hasura and Flutter

With GraphQL, you can define queries and mutations to fetch and modify data from the server.

import 'package:graphql_flutter/graphql_flutter.dart';

void getUsers() async {
final String getUsersQuery = '''
query {
users {
id
name
}
}
''';

final GraphQLClient client = GraphQLClient(
cache: GraphQLCache(),
link: HttpLink('https://your-hasura-endpoint.com/v1/graphql'),
);

final QueryResult result = await client.query(QueryOptions(
document: gql(getUsersQuery),
));

if (result.hasException) {
print(result.exception.toString());
} else {
final List<dynamic> users = result.data['users'];
for (var user in users) {
print('ID: ${user['id']}');
print('Name: ${user['name']}');
}
}
}

Conclusion

In this blog, we explored various database concepts in Flutter and learned how to implement them using different database technologies. We covered local data persistence, SQLite integration, Firebase Realtime Database, and GraphQL with Hasura.

With these skills, you can efficiently manage and store data in your Flutter applications. Experiment with these concepts and choose the most suitable database solution based on your app's requirements.

Happy coding!

Remember to import the necessary packages and dependencies to execute the code examples provided in this blog.

BUILDING MEMORY EFFICIENT FLUTTER APPS

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

In today's mobile app development landscape, memory efficiency plays a crucial role in delivering a smooth and responsive user experience. Flutter, Google's open-source UI toolkit, allows developers to create cross-platform apps with a rich set of features. However, as apps grow in complexity and data handling requirements, it becomes essential to optimize memory usage.

In this blog, we will explore some strategies and techniques to write memory efficient code in Flutter apps, ensuring optimal performance and user satisfaction.

1. Use Stateless Widgets

In Flutter, widgets are the building blocks of the UI. To conserve memory, prefer using StatelessWidget over StatefulWidget wherever possible. Stateless widgets are immutable and do not maintain any internal state. They consume less memory and are ideal for UI components that do not require frequent updates or interaction.

Example:

class MyWidget extends StatelessWidget {
final String data;

const MyWidget(this.data);

@override
Widget build(BuildContext context) {
return Text(data);
}
}

2. Dispose of Resources

When using resources like databases, network connections, or streams, it's crucial to release them properly to avoid memory leaks. Use the dispose() method provided by various Flutter classes to release resources when they are no longer needed. For example, in a StatefulWidget, override the dispose() method to clean up resources.

Example:

class MyStatefulPage extends StatefulWidget {
@override
_MyStatefulPageState createState() => _MyStatefulPageState();
}

class _MyStatefulPageState extends State<MyStatefulPage> {
DatabaseConnection _connection;

@override
void initState() {
super.initState();
_connection = DatabaseConnection();
}

@override
void dispose() {
_connection.close();
super.dispose();
}

// Rest of the widget code...
}

3. Use Efficient Data Structures

Choosing the right data structures can significantly impact memory consumption. Flutter provides various collections such as List, Set, and Map. However, be mindful of the memory requirements when dealing with large datasets. Consider using specialized collections like SplayTreeSet or LinkedHashMap that provide efficient look-up or iteration operations.

Example:

import 'dart:collection';

void main() {
var orderedSet = SplayTreeSet<String>();
orderedSet.addAll(['Apple', 'Banana', 'Orange']);

var linkedMap = LinkedHashMap<String, int>();
linkedMap['Alice'] = 25;
linkedMap['Bob'] = 30;
linkedMap['Charlie'] = 35;
}

4. Optimize Image Usage

Images often consume a significant portion of memory in mobile apps. To reduce memory usage, consider optimizing and compressing images before using them in your Flutter app. Tools like flutter_image_compress can help reduce the image size without compromising quality. Additionally, leverage techniques like lazy loading and caching to load images only when necessary.

Example:

import 'package:flutter_image_compress/flutter_image_compress.dart';

Future<void> compressImage() async {
var compressedImage = await FlutterImageCompress.compressWithFile(
'original.jpg',
quality: 85,
);

// Store or display the compressed image.
}

5. Use ListView.builder for Large Lists

When displaying large lists, prefer using ListView.builder instead of ListView to optimize memory usage. ListView.builder lazily creates and recycles widgets as they come into and go out of view. This approach avoids creating all the widgets upfront, conserving memory and improving performance.

Example:

ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
);

Conclusion

Writing memory efficient code is crucial for creating high-performance Flutter apps. By using stateless widgets, disposing of resources properly, leveraging efficient data structures, optimizing image usage, and utilizing ListView.builder, you can significantly reduce memory consumption and enhance the overall user experience. By adopting these practices, you'll be well on your way to building robust and efficient Flutter applications.

Remember, optimizing memory usage is an ongoing process, and profiling your app's memory consumption using tools like the Flutter DevTools can provide valuable insights for further improvements.

Happy coding!

A GUIDE TO UTILIZING MACHINE LEARNING FEATURES OF FLUTTER

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

Machine learning is revolutionizing mobile app development, enabling intelligent decision-making and enhancing user experiences. Flutter, the open-source UI toolkit from Google, offers a robust set of tools and libraries to seamlessly integrate machine learning capabilities into your applications.

In this blog post, we will dive into the practical aspects of utilizing Flutter's machine learning features, accompanied by relevant code samples.

1. Understanding Machine Learning Capabilities of Flutter

Flutter provides various machine learning options, including TensorFlow Lite, ML Kit, and community packages. These options allow developers to integrate machine learning models into their Flutter apps, leveraging pre-trained models or building custom models tailored to specific use cases.

2. Using TensorFlow Lite with Flutter

TensorFlow Lite is a lightweight framework for deploying machine learning models on mobile and embedded devices.

Let's explore how to use TensorFlow Lite with Flutter:

2.1 Model Selection

Choose a pre-trained TensorFlow Lite model or build a custom model using TensorFlow. Convert the model to TensorFlow Lite format. TensorFlow Hub is a great resource for finding pre-trained models for tasks like image recognition or natural language processing.

2.2 Integration

Add the TensorFlow Lite dependency to your Flutter project's pubspec.yaml file:

dependencies:flutter:sdk: flutter
tflite: ^X.X.X
# Replace with the latest version

2.3 Model Loading

Load the TensorFlow Lite model into your Flutter app using the TensorFlow Lite Flutter package. You can load the model from an asset file or a remote location:

import 'package:tflite/tflite.dart';

// Load the TensorFlow Lite model
await Tflite.loadModel(
model: 'assets/model.tflite',
labels: 'assets/labels.txt',
);

2.4 Model Inference

Perform inference with the loaded TensorFlow Lite model using input data and receive predictions or results:

List<dynamic> inference = await Tflite.runModelOnImage(
path: 'path_to_image.jpg',
numResults: 5,
);

// Process the inference results
inference.forEach((result) {
final label = result['label'];
final confidence = result['confidence'];
print('Label: $label, Confidence: $confidence');
});

3. Leveraging ML Kit for Flutter

ML Kit is a suite of machine learning capabilities provided by Google, simplifying the integration of machine learning models into mobile apps. Let's see how to use ML Kit with Flutter:

3.1 Integration

Add the ML Kit Flutter package as a dependency to your pubspec.yaml file:

dependencies:flutter:sdk: flutter
firebase_ml_vision: ^X.X.X
# Replace with the latest version

3.2 Model Selection

Choose the ML Kit model that suits your application requirements. For example, to incorporate text recognition, use the Text Recognition API.

3.3 Model Configuration

Configure the ML Kit model by specifying parameters such as language support, confidence thresholds, and other options.

3.4 Integration and Inference

Integrate the model into your app and perform inference using the ML Kit Flutter package:

import 'package:firebase_ml_vision/firebase_ml_vision.dart';

// Initialize the text recognizer
final textRecognizer = FirebaseVision.instance.textRecognizer();

// Process an image and extract text
final FirebaseVisionImage visionImage = FirebaseVisionImage.fromFilePath('path_to_image.jpg');
final VisionText visionText = await textRecognizer.processImage(visionImage);

// Extract text from the VisionText object
final extractedText = visionText.text;

// Perform additional processing with the extracted text
// ...

4. Exploring Flutter Community Packages

In addition to TensorFlow Lite and ML Kit, the Flutter community has developed various packages providing machine learning functionalities. These packages cover areas like natural language processing, image processing, recommendation systems, etc. Popular community packages include tflite_flutter, flutter_tflite, and flutter_native_image.

5. Custom Machine Learning Models with Flutter

If the available pre-trained models do not meet your specific requirements, you can build custom machine learning models using TensorFlow or other frameworks. Once trained and optimized, convert your model to TensorFlow Lite format and integrate it into your Flutter app using the steps outlined in Section 2.

Conclusion

Flutter's machine learning capabilities empower developers to create intelligent and feature-rich mobile applications.

By leveraging TensorFlow Lite, ML Kit, or community packages, you can seamlessly integrate machine learning models into your Flutter apps. The provided code samples serve as a starting point for your exploration of Flutter's machine learning features, opening up a realm of possibilities for creating innovative and smart mobile applications.