Explore a curated list of essential Flutter packages and plugins that enhance app development by adding functionality and simplifying complex tasks. Discover state management, networking, database, UI components, animations, internationalization, testing, and connectivity solutions.
In the dynamic world of Flutter development, leveraging the right packages and plugins can significantly enhance your productivity and the functionality of your applications. This section provides a curated list of essential Flutter packages and plugins, categorized by their primary functionality. Each package is accompanied by a description, usage scenarios, and practical code snippets to help you integrate them effectively into your projects.
State management is a crucial aspect of Flutter app development, ensuring that your app’s state is efficiently managed and updated across various components. Here are two highly recommended packages for state management:
Code Example:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Center(
child: Consumer<Counter>(
builder: (context, counter, _) => Text(
'${counter.count}',
style: TextStyle(fontSize: 48),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<Counter>().increment(),
child: Icon(Icons.add),
),
),
);
}
}
Code Example:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// Event
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// State
class CounterState {
final int count;
CounterState(this.count);
}
// Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0));
@override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
if (event is Increment) {
yield CounterState(state.count + 1);
}
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (context) => CounterBloc(),
child: CounterPage(),
),
);
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc Example')),
body: Center(
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'${state.count}',
style: TextStyle(fontSize: 48),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Icon(Icons.add),
),
);
}
}
Networking is a fundamental part of modern apps, enabling them to communicate with web services and APIs. Here are two popular packages for handling networking in Flutter:
http
package is a composable, Future-based library for making HTTP requests. It is simple to use and perfect for making REST API calls.Code Example:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('HTTP Example')),
body: Center(
child: FutureBuilder(
future: fetchAlbum(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Future<Album> fetchAlbum() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({required this.userId, required this.id, required this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Code Example:
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Dio Example')),
body: Center(
child: FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Future<String> fetchData() async {
try {
var response = await Dio().get('https://jsonplaceholder.typicode.com/posts/1');
return response.data['title'];
} catch (e) {
return 'Failed to load data';
}
}
Data persistence is essential for many applications, whether for storing user preferences or large datasets. Here are two popular packages for database and storage needs:
Code Example:
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Sqflite Example')),
body: Center(
child: FutureBuilder(
future: getDatabasesPath().then((path) => openDatabase(
join(path, 'demo.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE Test(id INTEGER PRIMARY KEY, name TEXT)",
);
},
version: 1,
)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text('Database created');
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Code Example:
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final appDocumentDir = await getApplicationDocumentsDirectory();
Hive.init(appDocumentDir.path);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Hive Example')),
body: Center(
child: FutureBuilder(
future: Hive.openBox('myBox').then((box) {
box.put('name', 'Flutter');
return box.get('name');
}),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text('Name: ${snapshot.data}');
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Enhancing the visual appeal of your app is crucial for user engagement. These packages provide powerful tools for UI components and styling:
Code Example:
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter SVG Example')),
body: Center(
child: SvgPicture.asset(
'assets/flutter_logo.svg',
width: 100,
height: 100,
),
),
),
);
}
}
Code Example:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Font Awesome Example')),
body: Center(
child: Icon(
FontAwesomeIcons.thumbsUp,
size: 50,
color: Colors.blue,
),
),
),
);
}
}
Animations can greatly enhance the user experience by providing visual feedback and engaging transitions. Here is a recommended package for adding animations:
Code Example:
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Lottie Example')),
body: Center(
child: Lottie.asset('assets/animation.json'),
),
),
);
}
}
Internationalization is essential for reaching a global audience. This package simplifies the process of localizing your Flutter applications:
Code Example:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Intl Example')),
body: Center(
child: Text(
Intl.message(
'Hello, World!',
name: 'helloWorld',
desc: 'The conventional newborn programmer greeting',
),
),
),
),
);
}
}
Testing and debugging are critical components of the development process, ensuring your app functions as expected. Here are two packages that facilitate testing and debugging:
Code Example:
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
// A simple class to demonstrate mocking
class Cat {
String sound() => 'Meow';
}
// Mock class
class MockCat extends Mock implements Cat {}
void main() {
test('Cat should make sound', () {
final cat = MockCat();
// Stub the method
when(cat.sound()).thenReturn('Purr');
expect(cat.sound(), 'Purr');
});
}
Code Example:
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Verify the counter starts at 0
expect(find.text('0'), findsOneWidget);
// Tap the '+' icon and trigger a frame
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify the counter increments
expect(find.text('1'), findsOneWidget);
});
}
Accessing device-specific features and connectivity options can greatly enhance your app’s functionality. Here are two packages that provide these capabilities:
Code Example:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Geolocator Example')),
body: Center(
child: FutureBuilder(
future: Geolocator.getCurrentPosition(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Text('Location: ${snapshot.data}');
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
Code Example:
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Permission Handler Example')),
body: Center(
child: ElevatedButton(
onPressed: () async {
var status = await Permission.camera.status;
if (!status.isGranted) {
await Permission.camera.request();
}
},
child: Text('Request Camera Permission'),
),
),
),
);
}
}
While integrating these packages can significantly enhance your app’s functionality and development speed, it’s important to consider the implications of adding dependencies:
By carefully selecting and integrating these packages, you can streamline your development process and create more robust, feature-rich applications.