Base64 Encoding & Decoding in Dart & Flutter
Dart's dart:convert library provides base64 and base64Url codecs built in. No external packages needed for standard Base64 in Dart or Flutter.
Import dart:convert
Everything you need for standard Base64 lives in Dart's core dart:convert library. It ships with the Dart SDK, so there is nothing to add to pubspec.yaml and nothing to run pub get for. A single import line exposes the base64 codec, the base64Url codec, and the utf8 codec you'll use to bridge between Dart strings and bytes.
import 'dart:convert';
The library exposes three Base64-related top-level constants. base64 is the standard codec, base64Url is the URL- and filename-safe variant, and base64Encode / base64Decode are convenience functions that delegate to the standard codec. Each codec object has an encode method that takes a List<int> of bytes and returns a String, and a decode method that does the reverse.
Base64 in Dart always operates on bytes (List<int> or Uint8List), never directly on strings. To encode a string you first convert it to bytes with utf8.encode, and to read decoded bytes back as text you use utf8.decode. This separation prevents accidental encoding bugs with non-ASCII characters.
Encode and decode strings
The most common task is round-tripping a text string. Because base64.encode expects bytes, you wrap your string in utf8.encode first. Decoding reverses both steps: base64.decode gives you bytes back, and utf8.decode turns those bytes into a readable string.
import 'dart:convert';
void main() {
final original = 'Hello, Dart! 🎯';
// Encode: String -> bytes -> Base64 String
final encoded = base64.encode(utf8.encode(original));
print(encoded); // SGVsbG8sIERhcnQhIPCfjq8=
// Decode: Base64 String -> bytes -> String
final decoded = utf8.decode(base64.decode(encoded));
print(decoded); // Hello, Dart! 🎯
}
Dart also provides the standalone helpers base64Encode(bytes) and base64Decode(string) if you prefer functions over the codec object. They are exactly equivalent to base64.encode and base64.decode, so use whichever reads better in your code.
final encoded = base64Encode(utf8.encode('quick helper'));
final bytes = base64Decode(encoded);
final text = utf8.decode(bytes);
Because Base64 is a fully reversible binary-to-text transform, the decoded bytes are byte-for-byte identical to the input. That makes it ideal for embedding binary payloads in JSON, configuration files, or anywhere only text is allowed. Emoji and other multi-byte UTF-8 characters survive the round trip unchanged, as the example above shows.
base64.decode throws a FormatException if the input is not valid Base64 — for example if it contains spaces, line breaks, or a length that isn't a multiple of four after padding. Wrap untrusted input in a try/catch block, and strip whitespace before decoding strings that may have been line-wrapped by another tool.
URL-safe Base64 with base64Url
Standard Base64 uses + and / as its 63rd and 64th characters, plus = for padding. Those characters have special meaning in URLs, query strings, and filenames, so dropping a standard Base64 string into a URL can break it. The base64Url codec swaps + for - and / for _, producing output that is safe to use without escaping.
import 'dart:convert';
void main() {
final bytes = utf8.encode('subjects?_d=qux');
final standard = base64.encode(bytes);
final urlSafe = base64Url.encode(bytes);
print(standard); // c3ViamVjdHM/X2Q9cXV4 (contains '/')
print(urlSafe); // c3ViamVjdHM_X2Q9cXV4 (uses '_')
}
This is the encoding used by JSON Web Tokens (JWTs), OAuth tokens, and many web APIs. Note that the JWT specification also strips the trailing = padding. Dart's base64Url keeps padding by default, so if you are hand-building a JWT segment you may need to remove it, and re-add it before decoding values produced elsewhere.
// Strip padding for a JWT-style segment
String b64UrlNoPad(List<int> bytes) =>
base64Url.encode(bytes).replaceAll('=', '');
// Decoding requires padding back to a multiple of 4
List<int> b64UrlDecode(String input) {
final padded = input.padRight((input.length + 3) ~/ 4 * 4, '=');
return base64Url.decode(padded);
}
base64.decode is lenient: it accepts both the standard and URL-safe alphabets and will decode - and _ correctly. So if you only need to decode a value and don't know which variant produced it, the plain base64 codec usually just works. Choose base64Url explicitly on the encode side when the output must travel through a URL.
Encode images in Flutter (Uint8List to Base64)
In Flutter, images are typically available as a Uint8List of raw bytes, whether they come from an asset bundle, the device file system, the camera, or a network request. Since Uint8List implements List<int>, you pass it straight to base64.encode with no conversion step.
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart' show rootBundle;
// From a bundled asset
Future<String> encodeAsset(String path) async {
final data = await rootBundle.load(path);
final bytes = data.buffer.asUint8List();
return base64.encode(bytes);
}
// From a file on disk (e.g. picked from the gallery)
Future<String> encodeFile(String imagePath) async {
final bytes = await File(imagePath).readAsBytes();
return base64.encode(bytes);
}
Once you have the Base64 string, a common use case is building a data URI to send to a backend or embed in HTML. Prefix the string with the appropriate MIME type. To display a Base64 image back in your Flutter UI, decode it to bytes and hand it to Image.memory, which renders directly from a Uint8List.
// Build a data URI
final base64String = await encodeFile(imagePath);
final dataUri = 'data:image/png;base64,$base64String';
// Render a Base64 image in the widget tree
Widget buildImage(String base64String) {
final bytes = base64.decode(base64String);
return Image.memory(bytes);
}
Base64 inflates data by roughly 33%, and encoding large images blocks the UI isolate while it runs. For multi-megabyte photos, run the encode on a background isolate using compute() from package:flutter/foundation.dart so you don't drop frames. For very large or frequently transferred files, prefer uploading the raw bytes via multipart instead of Base64.
Encode files with dart:io
On the Dart VM and in Flutter mobile/desktop apps, dart:io gives you File access. Reading a file as bytes and encoding it is two lines. For small files, readAsBytes loads everything into memory at once, which is fine. The asynchronous version is preferred so you don't block the event loop.
import 'dart:convert';
import 'dart:io';
Future<String> fileToBase64(String path) async {
final bytes = await File(path).readAsBytes();
return base64.encode(bytes);
}
// Decode Base64 back into a file
Future<void> base64ToFile(String b64, String outPath) async {
final bytes = base64.decode(b64);
await File(outPath).writeAsBytes(bytes);
}
For large files you should avoid loading the whole thing into memory. Dart's codecs are streaming-capable: you can pipe a file's byte stream through base64.encoder (a Converter) and write the result chunk by chunk. This keeps memory usage flat regardless of file size.
import 'dart:convert';
import 'dart:io';
Future<void> streamEncode(String inPath, String outPath) async {
final input = File(inPath).openRead();
final output = File(outPath).openWrite();
await input
.transform(base64.encoder) // Stream<List<int>> -> Stream<String>
.transform(utf8.encoder) // back to bytes for writing
.pipe(output);
}
dart:io is not available on Flutter Web. If your code needs to run in the browser too, encode bytes you already hold in memory (from http responses or file_picker) rather than touching File directly, or branch on kIsWeb from package:flutter/foundation.dart.
Send Base64 in HTTP requests with the http package
The http package is the standard way to make network calls in Dart. To send a Base64-encoded payload, encode your bytes, drop the resulting string into a JSON body, and POST it. The example below uploads an image as a Base64 field inside a JSON object.
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
Future<void> uploadImage(String imagePath) async {
final bytes = await File(imagePath).readAsBytes();
final base64Image = base64.encode(bytes);
final response = await http.post(
Uri.parse('https://api.example.com/upload'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'filename': 'photo.png',
'data': base64Image,
}),
);
if (response.statusCode == 200) {
print('Uploaded successfully');
} else {
print('Upload failed: ${response.statusCode}');
}
}
Receiving Base64 works the same way in reverse. Parse the JSON response, pull out the Base64 field, and decode it into bytes you can save or render. Because the server's value might use the URL-safe alphabet, decoding with the lenient base64 codec is the safest default.
Future<Uint8List> downloadImage(String url) async {
final response = await http.get(Uri.parse(url));
final json = jsonDecode(response.body) as Map<String, dynamic>;
final base64Image = json['data'] as String;
return base64.decode(base64Image);
}
If you control the API, prefer http.MultipartRequest for file uploads instead of Base64-in-JSON. Multipart sends the raw bytes, avoiding the ~33% size overhead and the cost of encoding/decoding on both ends. Reserve Base64-in-JSON for small payloads or APIs that explicitly require it.
FAQ
How do I Base64 encode a string in Dart?
Import dart:convert and use: import "dart:convert"; String encoded = base64.encode(utf8.encode("Hello")); String decoded = utf8.decode(base64.decode(encoded));
How do I encode an image to Base64 in Flutter?
Get the image as Uint8List (from AssetBundle, File, or NetworkImage), then encode: final bytes = await File(imagePath).readAsBytes(); final base64String = base64.encode(bytes); You can then use it as a data URI: "data:image/png;base64,$base64String"
What is the difference between base64 and base64Url in Dart?
base64 uses the standard Base64 alphabet (+ and /). base64Url uses the URL-safe alphabet (- and _) and is safe for use in URLs, filenames, and JWTs. Both are available in dart:convert without any additional packages.
Try Base64 encoding and decoding instantly
Paste any string or file — base64.dev auto-detects and converts it instantly.
Open base64.dev →