What is Chrome extension 🤔? : here
What is Flutter 🤔? : here
So First, create ⚡ a project in flutter
flutter create app_name
if flutter web is not enabled, then enable it by
flutter config --enable-web
to run the flutter app on the web 🌍, use
flutter run -d Chrome
Now we know that the config for running an app on the web is correct
Let's make some changes 🧑💻 in index.html
So, we can use the app as a Chrome extension.
In index.html
remove this from the head tag
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
and this from the body tag
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
}
}).then(function(engineInitializer) {
return engineInitializer.initializeEngine();
}).then(function(appRunner) {
return appRunner.runApp();
});
});
</script>
and add this script inside the body
<script src="main.dart.js" type="application/javascript"></script>
and also add style="height: 600px; width: 650px"
inside HTML tag
it will set the extension view's height and width to 600 pixels and 650
then open manifest.json
and replace all content with
more about manifest.json
: here
{
"name": "name of extension",
"description": "description for extension",
"version": "1.0.0",
"content_security_policy": {
"extension_pages": "script-src 'self' ; object-src 'self'"
},
"action": {
"default_popup": "index.html",
"default_icon": "icons/Icon-192.png"
},
"manifest_version": 3
}
and run flutter build web --web-renderer html --csp
this command will generate the web build as flutter web will dynamically generate code in the build output and this gives us CSP errors in the browser we have to use --csp
flag.
To learn more about the above command: doc
after this, the web build will be generated at build/web
to run 🏃♂️ the Chrome extension, open chrome and navigate to chrome://extensions/
and enable developer mode and click on load unpacked
open the web build from the path app_name/build/web
then open a new tab and click on the extension icon
yahoo…🥳
We made a basic counter extension in Flutter
Let's build 🔨 something useful 👩💻
let's build a simple base64 encoded and decoded 🔐
What is base64: here | how base64 works? : here
here is the full code in main.dart
the UI part is simple if you are familiar to flutter
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.grey,
),
home: const Base64());
}
}
class Base64 extends StatefulWidget {
const Base64({Key? key}) : super(key: key);
@override
_Base64State createState() => _Base64State();
}
class _Base64State extends State<Base64> {
TextEditingController textController = TextEditingController();
bool isEncode = true;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
const Padding(
padding: EdgeInsets.all(8.0),
child: Text("Base64"),
),
Container(
height: 25,
width: MediaQuery.of(context).size.width * 0.38,
decoration: BoxDecoration(
color: Colors.grey[600],
borderRadius: BorderRadius.circular(15)),
child: Row(
children: [
GestureDetector(
onTap: () {
if (isEncode == false) {
setState(() {
isEncode = !isEncode;
textController.clear();
});
}
},
child: Container(
height: 25,
width: MediaQuery.of(context).size.width * (0.38 / 2),
decoration: BoxDecoration(
color: isEncode == true
? Colors.black
: Colors.grey[600],
borderRadius: BorderRadius.circular(15)),
child: const Center(
child: Text(
"encode",
style: TextStyle(
color: Color.fromARGB(255, 225, 225, 225)),
)),
),
),
GestureDetector(
onTap: () {
if (isEncode == true) {
setState(() {
isEncode = !isEncode;
textController.clear();
});
}
},
child: Container(
height: 25,
width: MediaQuery.of(context).size.width * (0.38 / 2),
decoration: BoxDecoration(
color: isEncode == true
? Colors.grey[600]
: Colors.black,
borderRadius: BorderRadius.circular(15)),
child: const Center(
child: Text(
style: TextStyle(
color: Color.fromARGB(255, 225, 225, 225)),
"decode",
)),
),
),
],
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 13),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration(
border:
Border.all(color: Colors.black, style: BorderStyle.solid),
borderRadius: BorderRadius.circular(30),
),
child: ListTile(
trailing: GestureDetector(
onTap: () {
onTap(textController.text);
},
child: Container(
height: 40,
width: 80,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(30)),
child: Center(
child: Text(
isEncode == true ? "Encode" : "Decode",
style: const TextStyle(color: Colors.white),
)),
),
),
title: TextField(
controller: textController,
onChanged: (e) {},
decoration: const InputDecoration(
hintText: "Enter or Paste here ",
alignLabelWithHint: true,
border: InputBorder.none,
hintStyle: TextStyle()),
),
),
),
),
],
),
),
);
}
base64Encoder(String str) {
const base64 = Base64Codec();
List<int> bytes = utf8.encode(str.toString());
return base64.encode(bytes);
}
base64Decoder(String str) {
const base64 = Base64Codec();
Uint8List decode = base64.decode(str.toString());
return utf8.decode(decode);
}
onTap(String str) async {
FocusScope.of(context).unfocus();
String text = "";
if (isEncode == true) {
setState(() {
text = base64Encoder(textController.text);
});
} else {
setState(() {
text = base64Decoder(textController.text);
});
}
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("text copied to clipboard"),
duration: Duration(seconds: 1),
));
}
}
for encoding and decoding, we will use inbuilt API for conversion from
import 'dart:convert';
base64Encoder(String str) {
const base64 = Base64Codec();
List<int> bytes = utf8.encode(str.toString());
return base64.encode(bytes);
}
base64Decoder(String str) {
const base64 = Base64Codec();
Uint8List decode = base64.decode(str.toString());
return utf8.decode(decode);
}
and the onTap
function, first convert the string using the above function from textController
and then copy the text into the clipboard Clipboard.setData(ClipboardData(text: text));
from import 'package:flutter/services.dart';
onTap(String str) async {
String text = "";
if (isEncode == true) {
setState(() {
text = base64Encoder(textController.text);
});
} else {
setState(() {
text = base64Decoder(textController.text);
});
}
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("text copied to clipboard"),
duration: Duration(seconds: 1),
));
}
quick tips 💡 :
-
whenever we want to add a new change in the extension
use
flutter build web --web-renderer html --csp
to create a new build which has new changes and after that,
The extension by default does not save any state, so we can use local storage like hive DB it's a NoSQL DB for flutter which actually stores data in
Indexed DB
in browser
Source code: here | My GitHub: here
Hope 🤞 you have found this article interesting.
Keep learning! 👩💻 | More about me: here
Top comments (0)