It seems this question is there for many people. So, let me answer my own question.
Can flutter remember the network connection? Yes it can.
Flutter only require one network call to the same API to remember the connection. From the second call onward to the same API, it will use its "cached" memory giving you a big performance boost.
So first remember, this only works if you are calling the same API multiple times. If you are calling different APIs, this will not work. However in many apps, you have an API that built by the API team and you will be calling the same throughput the app.
The solution is to use flutter http.Client. Then share the same http.Client across the calls you make to the same API. You will see only first call takes time for "connection", rest of the calls do not take that time.
An example is available in flutter http pub page. It says ,
If you're making multiple requests to the same server, you can keep
open a persistent connection by using a Client rather than making
one-off requests. If you do this, make sure to close the client when
you're done:
Check below example. It is only for your reference, not the best way of using this.
main.dart
import 'package:flutter/material.dart';
import 'package:network_test/role_service.dart';
import 'package:network_test/user_role_service.dart';
import 'package:network_test/user_service.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var startTime = "";
var endTime = "";
void _network() async {
var client = http.Client();
RoleService _roleService = RoleService();
UserService _userService = UserService();
UserRoleService _userRoleService = UserRoleService();
String authToken = "****";
String uid = "555555";
try {
await _roleService.getAllRoles(authToken, client);
//await _roleService.getAllRoles(authToken, client);
await _userService.getUserByUID(authToken, uid, client);
await _userService.getUserByID(authToken, 27, client);
await _userRoleService.getUserRoleByUser(authToken, 27, client);
} finally {
client.close();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
"Start Time: " + startTime,
style: Theme.of(context).textTheme.headline4,
),
Text(
"End Time: " + endTime,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _network,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
role_service.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:network_test/role.dart';
import 'dart:convert' as convert;
import 'dart:io';
class RoleService with ChangeNotifier {
late List<Role> _roles;
String link2 = "https://api2.somewhere.com/userrel";
/// Return roles
List<Role> returnRoles() {
return _roles;
}
/// Get all Roles
Future<void> getAllRoles(String authToken, Client client) async {
try {
var data = await client.get(Uri.parse(link2 + "/role/getall"),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"});
var jsonData =
convert.json.decode(data.body).cast<Map<String, dynamic>>();
_roles = jsonData.map<Role>((json) => Role.fromJson(json)).toList();
print(_roles[0].roleName);
} catch (error) {
print(error);
throw error;
}
}
}
now I told you that above is not the best practice. Why? Because you will be creating and destroying the http.Client in many different places. Let's pay attention to a better practice.
In almost every app, we use State Management. I am a fan of Provider, it could be anything of your choice. i figured out the best way is to let the state management to remember the creation of http.Client. Since I am using Provider, I created the following class.
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class ConnectionService with ChangeNotifier {
http.Client _client = http.Client();
http.Client returnConnection() {
return _client;
}
}
And this is my main class
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ConnectionService()),
],
child: MyApp(),
));
}
Now when the app opens, I call the ConnectionService class to make the connection and do my API calls such as checking user authentication, user access etc. And only the first call is taking its time to build the connection, other calls do not.