This app is a collection of simple examples, that shows how the node-webkit.dart package can be used to build node-webkit apps with Dart.

It's built with dart2js (without the --minify argument), and packaged with the dart source files, so you can use Developer tools to navigate through the code. It also contains the source map generated by dart2js, so you can set breakpoints in dart code as well.

For more information, see https://github.com/luizmineo/node-webkit.dart

nw-gui

Import the nw-gui library to use the node-webkit API

 import 'package:node_webkit/nw_gui.dart' as gui;

Menubar

Use the Menu and MenuItem classes to create a menubar

  var windowMenu = new gui.Menu(type: "menubar");

  var submenu1 = new gui.Menu();
  var menu1 = new gui.MenuItem(label: "Menu 1", submenu: submenu1);

  submenu1.append(new gui.MenuItem(label: "Menu Item 1"));
  submenu1.append(new gui.MenuItem(label: "Menu Item 2"));
  submenu1.append(new gui.MenuItem(label: "Menu Item 3"));

  submenu1.items.forEach((item) {
    item.onClick.listen((event) => print("${item.label} selected!"));
  });

  windowMenu.append(menu1);

  new gui.Window.get().menu = windowMenu;

Context Menu

Use the Menu and MenuItem classes to create a context menu

  var contextMenu = new gui.Menu();

  contextMenu.append(new gui.MenuItem(label: "Menu Item 1"));
  contextMenu.append(new gui.MenuItem(label: "Menu Item 2"));
  contextMenu.append(new gui.MenuItem(label: "Menu Item 3"));

  contextMenu.items.forEach((item) {
    item.onClick.listen((event) => print("${item.label} selected!"));
  });

  //NOTE: this [window] instance comes from dart:html
  window.onContextMenu.listen((event) => contextMenu.popup(event.page.x, event.page.y));

Window

You can maximize, minimize and restore a window programmatically

  querySelector("#btn_maximize").onClick.listen((event) => new gui.Window.get().maximize());
  querySelector("#btn_unmaximize").onClick.listen((event) => new gui.Window.get().unmaximize());
  querySelector("#btn_minimize").onClick.listen((event) => new gui.Window.get().minimize());
  querySelector("#btn_restore").onClick.listen((event) {
    new gui.Window.get().minimize();
    new Timer(const Duration(seconds: 2), () => new gui.Window.get().restore());
  });
Fullscreen mode
  querySelector("#btn_fullscreen").onClick.listen((event) => new gui.Window.get().enterFullscreen());
  querySelector("#btn_leavefullscreen").onClick.listen((event) => new gui.Window.get().leaveFullscreen());
Kiosk mode
  querySelector("#btn_fullscreen").onClick.listen((event) => new gui.Window.get().enterFullscreen());
  querySelector("#btn_leavefullscreen").onClick.listen((event) => new gui.Window.get().leaveFullscreen());

Accessing the system clipboard

  querySelector("#btn_copyclipboard").onClick.listen((event) {
    new gui.Clipboard.get().data = (querySelector("#clipboard") as TextAreaElement).value;
  });
  querySelector("#btn_pasteclipboard").onClick.listen((event) {
    (querySelector("#clipboard") as TextAreaElement).value = new gui.Clipboard.get().data;
  });

Getting command line arguments

  querySelector("#commandline").text = "Command line args: ${gui.App.argv}";

node-filesystem

The node-filesystem library provides access to the functions of the fs module

  import 'package:node_webkit/node_filesystem.dart' as fs;

Reading files

Select a text file:

  var inputFileOrigin = querySelector("#input_file_origin1");
  inputFileOrigin.onChange.listen((event) {
    var filePath = fs.getPath(inputFileOrigin.files[0]);
    fs.readFileAsString(filePath).then((text) {
      (querySelector("#textarea_filecontent1") as TextAreaElement).value = text;
    });
  });

Writing files

Warning: If the file already exists, it will be overwritten
  var inputFileDestiny = querySelector("#input_file_destiny");
  var textElement = querySelector("#textarea_filecontent");
  querySelector("#btn_nodesavefile").onClick.listen((event) {
    if(inputFileDestiny.files.isEmpty) {
      return;
    }
    var filePath = fs.getPath(inputFileDestiny.files[0]);
    fs.writeFileAsString(filePath, textElement.value).then((_) => print("file $filePath saved"));
  });

dart-filesystem

The dart-filesystem complements the node-filesystem library, and provides the FileSystemEntity, File, Directory and Link classes, so you can access the filesystem just as you would do with dart:io

  import 'package:node_webkit/dart_filesystem.dart';

Reading files line by line

Select a text file:

  var dartInputFile = querySelector("#input_file_origin2");
  var dartTextElement = querySelector("#textarea_filecontent2");
  dartInputFile.onChange.listen((event) {
    var filePath = fs.getPath(dartInputFile.files[0]);
    dartTextElement.value = "";

    var file = new File(filePath);
    file.openReadAsString()
        .transform(new LineSplitter())
        .listen((line) {
          dartTextElement.value += "$line\n";
        },
        onDone: () => print("Finished reading file $filePath"),
        onError: (err) => print("Failed to read file $filePath: $err"));
  });
      

Also, you can use the classes of dart:convert package to transform from bytes to String

  var dartInputFile = querySelector("#input_file_origin2");
  var dartTextElement = querySelector("#textarea_filecontent2");
  dartInputFile.onChange.listen((event) {
    var filePath = fs.getPath(dartInputFile.files[0]);
    dartTextElement.value = "";

    var file = new File(filePath);
    file.openRead()
        .transform(UTF8.decoder)
        .transform(new LineSplitter())
        .listen((line) {
          dartTextElement.value += "$line\n";
        },
        onDone: () => print("Finished reading file $filePath"),
        onError: (err) => print("Failed to read file $filePath: $err"));
  });

Listing directories

Select a directory:

Recursive
  var inputDir = querySelector("#input_file_dir");
  var textElementDir = querySelector("#textarea_dircontent");
  var checkboxRecursive = querySelector("#recursive");

  var buttonListDir = querySelector("#btn_listdir");
  var buttonPause = querySelector("#btn_pauselistdir");
  var buttonCancel = querySelector("#btn_cancellistdir");
  var buttonResume = querySelector("#btn_resumelistdir");

  var subscription;

  buttonListDir.onClick.listen((event) {
    if (inputDir.files.isEmpty) {
      return;
    }
    var dirPath = fs.getPath(inputDir.files[0]);
    textElementDir.value = "";
    
    Directory dir = new Directory(dirPath);
    subscription = dir.list(recursive: checkboxRecursive.checked)
      .listen((entity) {
          textElementDir.value += "${entity is File ? 'File' : 'Directory'}: ${entity.path}\n";
        }, 
        onError: (err) => print("Error received while listing $dirPath: $err"), 
        onDone: () {
          print("Finished listing $dirPath");
          buttonPause.disabled = true;
          buttonResume.disabled = true;
          buttonCancel.disabled = true;
          buttonListDir.disabled = false;
        }
      );

    buttonListDir.disabled = true;
    buttonPause.disabled = false;
    buttonCancel.disabled = false;
  });

  buttonPause.onClick.listen((event) {
    subscription.pause();
    buttonPause.disabled = true;
    buttonResume.disabled = false;
  });

  buttonCancel.onClick.listen((event) {
    subscription.cancel();
    buttonPause.disabled = true;
    buttonResume.disabled = true;
    buttonCancel.disabled = true;
    buttonListDir.disabled = false;
  });

  buttonResume.onClick.listen((event) {
    subscription.resume();
    buttonPause.disabled = false;
    buttonResume.disabled = true;
  });
      

Copying files

Warning: If the destiny file already exists, it will be overwritten

  var inputFileFrom = querySelector("#input_file_copy_from");
  var inputFileTo = querySelector("#input_file_copy_to");
  var buttonCopy = querySelector("#btn_copy_file");
  var buttonCopySync = querySelector("#btn_copysync_file");
  var progress = querySelector("#copy_progress");

  buttonCopy.onClick.listen((event) {

    if (inputFileFrom.files.isEmpty || inputFileTo.files.isEmpty) {
      return;
    }

    File fileFrom = new File(fs.getPath(inputFileFrom.files[0]));
    String fileTo = fs.getPath(inputFileTo.files[0]);

    fileFrom.copy(fileTo).then((_) {
      print("File ${fileFrom.path} copied to $fileTo");
      progress.text = "File ${fileFrom.path} copied to $fileTo";
    }).catchError((err) {
      print(err);
      progress.text = "Error: $err";
    }).whenComplete(() {
      buttonCopy.disabled = false;
      buttonCopySync.disabled = false;
    });
    
    progress.text = "Copying file...";
    buttonCopy.disabled = true;
    buttonCopySync.disabled = true;
  });

  buttonCopySync.onClick.listen((event) {

    if (inputFileFrom.files.isEmpty || inputFileTo.files.isEmpty) {
      return;
    }

    File fileFrom = new File(fs.getPath(inputFileFrom.files[0]));
    String fileTo = fs.getPath(inputFileTo.files[0]);

    progress.text = "Copying file...";
    buttonCopy.disabled = true;
    buttonCopySync.disabled = true;

    new Future(() {
      try {
        fileFrom.copySync(fileTo);

        print("File ${fileFrom.path} copied to $fileTo");
        progress.text = "File ${fileFrom.path} copied to $fileTo";

      } catch (err) {
        print(err);
        progress.text = "Error: $err";      
      }
    }).then((_) {
      buttonCopy.disabled = false;
      buttonCopySync.disabled = false;
    });
  });