DartBlock

pub package

Introduction

A block-based programming framework, primarily designed for mobile devices.

In addition to a drag-and-drop based approach to composing programs, DartBlock also features several functionalities which are primarily meant to facilitate the framework's integration in the context of quizzes:

  • Automatic evaluation: several schemas are available, based on which a user's program should be automatically checked for correctness.
  • Execution: DartBlock programs can directly be executed on device to view their output.
  • Exceptions: faulty programs can throw exceptions during their execution, which can help to teach debugging.
  • Difficulty tuning: DartBlock offers levers to shuffle a given program's list of statements, as well as to trim it.
    • Example use case: to make a programming question based on DartBlock easier, part of the solution could be provided to the user, or the full solution could be provided but in a shuffled state.
  • Serialization: with support for JSON encoding and decoding, DartBlock programs can be stored and reloaded.

Example

import 'package:flutter/material.dart';
import 'package:dartblock_code/dartblock_code.dart';

final program = DartBlockProgram.example();
void main() => runApp(
  const MaterialApp(
    home: Material(
      child: Center(
        child: CustomScrollView(slivers: [
            SliverFillRemaining(
              child: DartBlockEditor(
                key: ValueKey(program),
                program: program,
                canChange: true,
                canDelete: true,
                canReorder: true,
                canRun: true,
              ),
            ),
          ]),
      ),
    ),
  ),
);

A more comprehensive working example can be found under example/.

Usage

Editor

The DartBlockEditor widget is the main component used for both rendering and editing a DartBlock program.

var program = DartBlockProgram.init([], []);
DartBlockEditor(
  key: ValueKey(program),
  program: program,
  canChange: true, // Create or edit statements/functions
  canDelete: true, // Delete existing statements/functions
  canReorder: true, // Re-order existing statements
  canRun: true, // Show "Run" button
  /// After a set amount of time (maximumExecutionDuration), DartBlock will
  /// automatically interrupt the execution of a DartBlockProgram.
  maximumExecutionDuration: Duration(seconds: 10), // default: 5s
  isDense = false, // default: false
  scrollController: null,
  onChanged: (changedDartBlockProgram) {
    // ...
  },
  onInteraction: (dartBlockInteraction) {
    // example: user tapped on "Run" button
    // Useful for statistics or logging
  },
  padding: const EdgeInsets.all(4), // default: null
)
DartBlock program editor

If you simply want to render a DartBlockProgram which the user can execute, but otherwise not modify, then use the following:

var program = DartBlockProgram.init([], []);
DartBlockEditor(
  key: ValueKey(program),
  program: program,
  canChange: false,
  canDelete: false,
  canReorder: false,
  canRun: true,
)
DartBlock program view

Evaluator

To set-up the evaluation schemas for a given DartBlockProgram, use the DartBlockEvaluator widget:

DartBlockEvaluator? evaluator;
DartBlockProgram sampleSolution = DartBlockProgram.init([], []); // Placeholder, your input program should not be empty.
DartBlockEvaluatorEditor(
  evaluator: evaluator,
  sampleSolution: sampleSolution,
  onChange: (evaluator) {
    setState(() {
      this.evaluator = evaluator;
    });
  },
)

This widget displays a dropdown button for selecting which evaluation schema to add to the DartBlockEvaluator, as well as the editors for each schema.

DartBlock evaluation schema editor

Evaluation Result

To display the evaluation of a given input DartBlockProgram using your sample solution DartBlockProgram and DartBlockEvaluator, use the DartBlockEvaluationResultWidget widget:

// DartBlockEvaluator evaluator = ...;
// DartBlockProgram sampleSolution = ...;
// DartBlockProgram inputProgram = ...;
FutureBuilder(
  future: evaluator!.evaluate( // evaluate() is an async function!
    sampleSolution,
    inputProgram,
  ),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.active ||
        snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 4),
            Text("Evaluating..."),
          ],
        ),
      );
    } else if (snapshot.hasData) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            "Evaluation Result",
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          SizedBox(height: 4),
          DartBlockEvaluationResultWidget(result: snapshot.data!), // Display result of evaluation
        ],
      );
    } else {
      return const Text(
        "Sorry, there was an issue evaluating your program.",
        textAlign: TextAlign.center,
      );
    }
  },
),
DartBlock program evaluation result

Features

Data Types

  • int
  • double
  • String
  • boolean

Building Blocks

The supported set of "blocks" or statement types.

  • Variables
    • Declare Variable
    • Update Variable
  • Loops
    • For-loop
    • While-loop / Do-while-loop
  • Decision structures
    • If-then-else
  • Print
  • Function Call
  • Return Value
  • Break
  • Continue
  • DartBlock statement type picker

Custom Functions

In addition to the default main function which serves as the entry point of a DartBlock program, custom functions can also be defined, including:

  • Return type ("void" included)
  • List of parameters
  • Name (unique)
DartBlock custom function editor

Program Execution

DartBlock programs can be executed on device to verify their behavior and view their output.
DartBlock is console-based, with support for "Print" statements to print messages to said console.

Execution occurs on a separate isolate, such that the main isolate rendering the UI is not frozen while the execution is happening. However, web support in this regard is more limited. (see below "Supported Platforms")
Execution is also timed, such that a faulty program does not end up executing indefinitely, e.g., due to a recursive function without a proper ending condition. After a set amount of time, DartBlock will automatically interrupt the execution.

DartBlock program execution result in console

Exceptions

DartBlock supports exceptions, which can interrupt a program's execution.
The details of an exception are directly shown within the console, and the relevant statement which caused the exception to be thrown is highlighted to facilitate debugging.

DartBlock program export to java

Export to Java

DartBlock programs can be exported to the conventional typed language "Java".

DartBlock program export to java

Program Evaluation

DartBlock offers a set of so-called evaluation schemas, which can enable the automatic evaluation of a DartBlock program.

These include:

  • Variable Count: check if an input DartBlock program contains fewer than or exactly the same number of variable declarations as a given sample solution (DartBlock program).
  • Script: check if the Java version of the user's DartBlock program matches the Java version of the sample solution. This is a purely String-based comparison using the Damerau-Levenshtein metric, with the similarity threshold being customizable.
  • Function Definition: check if a set of custom functions has been defined, based on their name, list of parameters and return type. The actual behavior and output of the functions is not verified.
  • Function Output: check if a set of sample function calls result in the same output when using the input program and the sample solution program. This is a stricter variant of the 'Function Definition' schema.
  • Print: check if, post-execution, the input DartBlock program has the same console output (stemming from the usage of "Print" statements) as the sample solution. The order of output is taken into account.
  • Environment: check if, post-execution, the input DartBlock program leads to the same variable declarations, including their assigned values, as the sample solution.

The first 3 schemas do not require the execution of the input program to check for correctness, i.e., they are static validators.
The 3 remaining schemas require the execution of the input program to check for correctness, where the check can also result in a failure if an exception is thrown during the execution.

Multiple schemas can also be combined for the automatic evaluation, and DartBlock comes pre-packaged with widgets to create and edit these schemas for a given DartBlock program.

Serialization

DartBlock supports encoding DartBlock programs to JSON, as well as decoding DartBlock programs from JSON.

DartBlockProgram program = DartBlockProgram.init([], []);

/// Encode as JSON:
Map<String, dynamic> encodedAsJson = program.toJson();
// afterwards, you could for example save this JSON encoding to disk as a simple String object!

/// Decode from JSON: ideally, you should wrap this in a try-catch!
DartBlockProgram decodedFromJson = DartBlockProgram.fromJson(encodedAsJson);

Supported Platforms

  • iOS
  • Android
  • MacOS
  • Windows
  • Linux
  • Web* (limited)

Web

DartBlock supports a broad range of its feature set on the Web platform.
However, due to Dart not supporting Isolates on Web, DartBlock's execution model is more rudimentary:

  • Execution of a DartBlock program occurs on the main isolate, which is also used to render the containing Flutter app. Subsequently, the UI will remain frozen until the execution is terminated.
  • A faulty DartBlock program can freeze the containing app. For example, if the executed program contains a recursive function call with no proper ending condition, the program's execution will never end.
    • On non-Web platforms, DartBlock executes programs on a separate isolate, which can easily be killed after a set amount of time if the execution has not finished.

For more info, see the function _executeWeb() in lib/core/dartblock_executor.dart.

Documentation

This package contains some preliminary documentation for its main classes. Additional documentation will be added gradually!

Assets

If you use DartBlock, please consider citing the corresponding thesis and/or paper on it:

Libraries

core/dartblock_executor
core/dartblock_program
dartblock_code
models/dartblock_interaction
models/dartblock_notification
models/dartblock_validator
models/dartblock_value
models/environment
models/evaluator
models/exception
models/function
models/help
models/statement
widgets/dartblock_editor
widgets/dartblock_value_widgets
widgets/editors/composers/boolean_value
widgets/editors/composers/components/algebraic_digit_button
widgets/editors/composers/components/algebraic_dot_button
widgets/editors/composers/components/algebraic_operator_button
widgets/editors/composers/dartblock_value
widgets/editors/composers/number_value
widgets/editors/composers/string_value
widgets/editors/composers/value_concatenation
widgets/editors/custom_function_basic
widgets/editors/dartblock_data_type_picker
widgets/editors/for_loop
widgets/editors/function_call
widgets/editors/if_else_then
widgets/editors/misc
widgets/editors/print
widgets/editors/return
widgets/editors/statement
widgets/editors/variable_assignment
widgets/editors/variable_declaration
widgets/editors/variable_definition
widgets/editors/while_loop
widgets/evaluator_editor
widgets/helper_widgets
widgets/views/custom_function
widgets/views/evaluation/evaluation_widget
widgets/views/for_loop
widgets/views/function_call
widgets/views/function_definition
widgets/views/if_else_then
widgets/views/other/dartblock_colors
widgets/views/other/dartblock_console
widgets/views/other/dartblock_exception
widgets/views/other/help_center
widgets/views/statement
widgets/views/statement_listview
widgets/views/symbols
widgets/views/variable_assignment
widgets/views/variable_declaration
widgets/views/variable_definition
widgets/views/while_loop
widgets/widgets