How to create a custom DartPad?

Akshat Sharma
Flutter Clan
Published in
7 min readJun 4, 2021

We all love DartPad. As soon as you start your journey with Flutter, you’ll come across this online editor to try out your dart ideas and solutions. I myself have tried numerous code snippets on DartPad whenever I felt too lazy to create a simple new Flutter project on my local and run it on a simulator or emulator.

It is truly amazing to use the power of Flutter on the web. That means I can write my code instantly on my tablet or even my mobile. But sadly as of today, we can only use core libraries while writing code in DartPad.

So this is the problem statement: We want to use different libraries on pub.dev in DartPad.

According to me, understanding how to reach a solution is more important than knowing the solution. In this article, we will reach a solution so that while implementing you can improvise according to your needs.

One second, if you are thinking that you’ll be needing prior knowledge of compiler design for this article, then mate you are absolutely wrong. All you need to know is Dart. 🤟🏻

Let’s go!!

Internally, DartPad uses DartServices to function. Creators of DartPad are using it for only UI purposes. They give an interface for an editor, the output, and few other configurations to run gist files. The code written in DartPad is sent to DartServices for compilation. DartServices returns javascript back to DartPad which gets rendered on the output panel using iFrame. This means that libraries requiring native mobile support won’t work in our case. Flutter web is the key here.

First, let’s set up our DartPad and DartServices.

Setting up your machine

Download or clone the dart-pad and dart-service.

Install NPM.

brew install node

Install vulcanize.

npm install -g vulcanize

I am pretty sure if you are reading this article, Flutter is already set up on your machine, if not then follow the instructions provided here.

Setting up local DartPad

On the terminal, go to the downloaded or cloned folder of dart-pad and run the following commands one by one.

dart pub global activate grinderdart pub getgrind serve

Now hold on for some time. It takes a couple of minutes before the server is up and running. Once it is up, go to localhost:8000 run your first dart code on your local machine. Cheers!!🍻

Now open the network tab of your browser. You’ll see a couple of requests made like version, analyse, & complile, etc. All of these requests are being made to DartServices. This means that the next step will be to point our local DartPad to our local DartServices server. To do this, go to build.yaml file present at the root level of your project and change the server URL to which you want your DartPad to point.

Pointing dart-pad to local dart-services server

Again when you execute the following command,

grind serve

You’ll see that now your DartPad is making requests to 0.0.0.0:8082 which is not returning any response. You can use the custom port as per availability on your machine. Make sure to configure it while setting up the DartServices server.

Setting up local DartServices server

On the terminal, go to the downloaded or cloned folder of dart-services and run the following commands one by one.

brew install protobuf (`brew upgrade protobuf` if already installer)pub global activate grinderpub global activate protoc_plugindart pub installdart run tool/update_sdk.dart

Before running the server, we’ll have to make sure on which address and port our server will be listening to. Go to lib/services_dev.dart and set 0.0.0.0 as server-url.

If you want your server to point to some other port then go to tool/grind.dart and change the port. By default, the port is set to 8082.

[Optional] Just to be extra sure about the change print the server address.

Now, go ahead and run

grind serve

You’ll see a lot of logs in your terminal and in that logs, you’ll find

Cheers!! 🍻 Your DartServices server is up and running. Now your local DartPad which is pointing to the above-served server will run just like https://dartpad.dev/.

Adding support for pub.dev packages

The above documentation is also available on Github. The real game begins now.

Stage 1

Run the following code on your local DartPad

import 'package:flutter/material.dart';void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello World',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
color: Colors.red,
),
],
),
),
);
}
}

The above code will give the following output.

Stage 2

Now go ahead and add the dependency you want to use. In this example, we are going to use dots_indicator.

import 'package:flutter/material.dart';
import 'package:dots_indicator/dots_indicator.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello World',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
color: Colors.red,
),
],
),
),
);
}
}

Our DartServices does not understand what dots_indicator is hence it gives us an error that target URI doesn’t exist.

Here is our first clue for our problem. We want our server to understand what we mean by dots_indicator. Now think that you are given some text which you need to understand, what will you do? You’ll start from the top and start understanding everything lines by line. Similarly, our server tries to understand each line. Creators of DartServices have limited the usage of external 3rd party dependencies. They simply find all the imports and check if the import belongs to the set of approved imports or not. If not then they straightaway give the error which we received. Until now dart compiler hasn’t come into the picture.

Solution of the problem

We need to find a way to bypass this security check. If you dig down the code of dart services you’ll find the list of pre-approved imports. Go to lib/src/project.dart. Here you’ll find a list of_flutterImportPrefixes. Add dots_indicator to the list as well.

Now run the server again. And you’ll see a different error of Unsupported import which is returned by the flutter compiler.

Stage 3

Now the problem is that we need our server to understand all the capabilities of dots_indicator. Generally in our Flutter project, we do this by adding the dependency in pubspec.yaml file and once we run flutter pub get , all the dependencies are fetched and are ready to use.

Solution of the problem

If you read the logs when we run the server by command grind serve , it actually runs flutter pub get a couple of times in different directories, one of which is flutter_project. As soon as we run the project, Dart-Services creates a project with custom pubspec and run flutter pub get in that directory. Now the code written in the editor of DartPad is sent to DartServices which runs it as main.dart of this flutter_project.

Now go to file tool/grind.dart and function updateDependenciesFile and add dots_indicator to the list of dependencies.

After making this change, run the server again using grind serve

Now our code won’t give an error. Anyways it will be giving a warning which is as expected that is Unused import.

You can now start using dots_indicator

import 'package:flutter/material.dart';
import 'package:dots_indicator/dots_indicator.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello World',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 200,
color: Colors.red,
),
DotsIndicator(
dotsCount: 5,
position: 1,
),
],
),
),
);
}
}

which will execute and give result as follows

Congratulations!

Using the above approach we can create our own DartPad supporting custom libraries from pub.dev. Happy coding! Until next time.

--

--