- Dart:Scalable Application Development
- Davy Mitchell Sergey Akopkokhyants Ivo Balbaert
- 2720字
- 2021-07-09 18:56:16
Building the dialog package
The pub
packaging system is a great resource for sharing finished packages online. If we want to keep a package local and private, we can create it locally and use it in much the same manner.
The package project structure
The package project is structured in a similar manner to a web application project, with a pubspec.yaml
file being the main project file. Instead of a web folder, there is a lib
folder containing the simple_dialog.dart
file that declares our library and defines what is exposed outside the project:

This file contains any classes, functions, and references to other files—in this case, the two files in the src
folder. The first line in simple_dialog.dart
with the keyword library
states the name and declares that this is a library. Note that it is not surrounded by any quotation marks:
library simple_dialog; import 'dart:html'; part 'src/dialog_base.dart'; part 'src/prefabs.dart';
The next section contains the imports required for all the files that are being exposed by this package declaration in the part declarations. Open either of the files in the src
folder and you will see part of simple_dialog
declared on the first line.
Adding a local package reference
To reference the library, go to the TextEditor
project's pubspec.yaml
and open it in the editor. Click on the source tab to see the plain yaml
text file. There is currently no graphical interface for the local reference, so it has to be added by hand:
dependencies: browser: any intl: any simple_dialog: path: ..\simple_dialog
The path is relative to the root project directory, and, as this is a local filesystem reference, the path separator must match the operating system, so we use \
(backslash) for Windows and /
(forward slash) for Linux and Mac.
The package is then referenced in editor.dart
with a package
prefix:
import 'dart:convert'; import 'dart:html'; import 'package:simple_dialog/simple_dialog.dart';
It operates in exactly the same way as a remotely installed package, with the added advantage that it automatically refreshes if changes are made.
Note
To avoid any problems with relative paths (which make many people's heads hurt!), if possible, keep projects in the same directory so that the relative path is always the same. Alternatively, full paths can be used, although this may introduce problems when the code is on different computers.
To verify that the path is right, look in the Project
tab, expand the Packages
folder, and the package should be listed with its source.
Path dependencies are useful for local development, but will not work when sharing projects with the wider world.
Dart operates under a library scoping system. All items in a package are available throughout—there are no private or protected assets at this level. However, items inside the library can be kept private to external code.
To achieve the equivalent of a private field, a field of a class can be marked private if it has a preceding _
(underscore) character. Any project referencing the library can only use the assets that the package has exposed.
For example, the TextEditor
project can use the Dialog
class that is public by default and access the content
field, but not the _visible
field.
The dialog core is defined in the Dialog
class in the dialog_base.dart
file. This will contain all the foundational functionality, such as the dialog frame and the OK and CANCEL buttons. More specialized dialog boxes will inherit (extend
) this class and modify it for their purposes.
The package will contain common dialogs for the following:
This is the simplest of all the dialogs. It will show a line of text and allow the user to press the OK button so that the dialog box is dismissed:
void alert(String dlgTitle, String prompt, int width, int height) { Dialog dlg = new Dialog(dlgTitle, prompt, width, height, true, false); dlg.show(); }
The dialog box does not return any value, and to make this as easy as possible for the user, it is a single function call. This will be used to display the result of the word-count feature.
The standard About
box for an application contains a small amount of information and a link to a website. This dialog is visually identical to the Alert
box apart from the addition of the hyperlink, and as this involves changing the dialog interface elements, this will be implemented in a new class called AboutDialog
:

This is created from the showAbout
method of the TextEditor
object:
void showAbout(MouseEvent event) { AboutDialog textEditorAbout = new AboutDialog(AppTitle, "TextEditor for the Web", "http://www.packtpub.com", "Homepage", 300, 200); textEditorAbout.show(); }
The AboutDialog
box inherits from the Dialog
class and calls the constructor of the Dialog
class via super
, which calls the constructor to form the foundation of the object:
AboutDialog(String titleText, String bodyText, this.linkUrl, this.linkText, int width, int height) : super(titleText, bodyText, width, height, true, false){
The elements for the box are created and added to the nodes member of the content DivElement
:
content..nodes.insert(0, new BRElement()) ..nodes.insert(0, new BRElement()) ..append(new BRElement()) ..append(new BRElement()) ..append(link) ..style.textAlign = "center";
The cascade
operator keeps the code compact and focused on setting up the different components of the dialog.
This dialog presents OK and CANCEL options, and calls a specified function if the former option is clicked. This is used in the TextEditor
class clearEditor
method:

This is implemented by using the base Dialog
class directly:
void confirm(String dlgTitle, String prompt, int w, int h, Function action) { Dialog dlg = new Dialog(dlgTitle, prompt, w, h); dlg.show(action); }
The application can configure and display this dialog with a single line:
confirm(AppTitle, "Are you sure you want to clear the text?", 400, 120, performClear);
A function is passed to the show
method that is performed if the user presses the OK button.
Word count is an important feature for students, technical writers, and competition entrants. Our customers see this as a required feature, too.
In the event handler, we want to calculate the word count and throw the dialog on screen for the user to see. This is a simple, short-running task, so the implementation can exist directly in the event handler. As the output is simple, the alert dialog can be used.
To determine the word count, Dart's List
data structure and String
classes can be used. The first step is to remove any punctuation characters that may affect our count. This is declared in custom_dialogs.dart
as an unmodifiable const
string as it can be used for other word-based features:
const String punctuation = ",.-!\"";
This is used in the showWordCount
method of the TextEditor
class.
Once the punctuation is removed, the string can be split into a List
. Dart supports generics, so the types of general data structure can be optionally specified, in this case with <String>
. The list of words is then filtered to clean out any non-word entries remaining.
Once we have obtained the list of words, the final word count can be obtained by using the length
property of the list.
Our customers would like a feature to help with writing quality. Overuse of particular words can make a text repetitive and hard to read. To determine the frequency of word usage, Dart's Map
(sometimes called an associative array or dictionary), data structure class, and String
features can be used:

Depending on the number of different words used, the content areas may be allowed to scroll, as the dialog cannot grow to the size of any data.
Generics are used again to specify the types for the key and value; in this case, String
for the key and int
for the value:
Map<String, int> wordFreqCounts = {}; String out = ""; for (var character in punctuation.split('')) text = text.replaceAll(character, " "); text = text.toLowerCase(); List<String> words = text.split(" "); words ..removeWhere((word) => word == " ") ..removeWhere((word) => word.length == 0) ..forEach((word) { if (wordFreqCounts.containsKey(word)) wordFreqCounts[word] = wordFreqCounts[word] + 1; else wordFreqCounts[word] = 1; }); wordFreqCounts .forEach((k, v) => out += ("<tr><td>$k</td><td>$v</td></tr>"));
The processing of the text is similar to the word count, where a list of individual words is built up. Once the list is filtered, forEach
is used to iterate through the list and count the occurrences of each word. The forEach
method is used again to build the output HTML, with the map's key and value parameters being passed to the function.
Understanding the typing of Dart code
The types used so far for the Map
and List
help the design-time tools, such as the Dart Analyzer and WebStorm, which allows us to write code by providing the code completion and validating assignments.
When run in Dartium with the checked mode enabled (the default setting), types are checked at runtime. In the final environment, most likely a web browser such as Firefox or Internet Explorer, the typing information is not used and does not affect the execution speed of the application.
Dart coding recommendations advise using types on all public-facing code. For example, a class in a package can use private var
variables, but methods require and return typed data. This aids developers calling the code in understanding what is required, and helps create better documentation.
The file download feature
It can be very useful to have an actual file, so we will add a feature to let the user download and save the contents of the editor to their local device. This is possible with the custom data attributes feature in HTML5, which supports text as one of the formats:
void downloadFileToClient(String filename, String text) { AnchorElement tempLink = document.createElement('a'); tempLink ..attributes['href'] = 'data:text/plain;charset=utf-8,' + Uri.encodeComponent(text) ..attributes['download'] = filename ..click(); }
The process is straightforward: an anchor element is created and its target is set as the encoded text data. The click event is then fired and the download proceeds as if it were being downloaded from a web server.
Everyone has deadlines, so keeping an eye on the clock is important. The dart:async
package can help with this task by using a Timer
class to trigger and update. This can be carried out in one line in main.dart
file of the TextEditor
project:
new Timer.periodic(newTimer.periodic(new Duration(seconds: 1), (timer) => querySelector("#clock").text = (new DateFormat('HH:mm')) .format(new DateTime.now()) );
The Timer
constructor being called here (Timer.periodic
) is a specially named constructor; in this case, the periodic
version is being used. There are numerous variations of Timer
objects. Named constructors allow the classes to have instances easily created for a particular purpose as they do not require additional initialization configuration before they are ready to use.
A periodic Timer
will fire an event every duration period; in this case, one second. The remainder of the line declares the function that is called. Dart has a shorthand of =>
for functions that are a single line (which are named arrow functions), thus avoiding the use of curly brackets. The querySelector
function is used to get a reference to the element on the web page with an ID of clock
where the time will be displayed.
The current time is obtained and formatted into hours and minutes for display. As the clock will run for the lifetime of the application and does not require any interaction, no variable is required to store a reference to the object.
That was a busy line of code, and very powerful, but we are not finished examining it yet! There has been no extra thread or process created. This is not a user-initiated event. We will take a look at the what and when of the Dart task queue.
The internal architecture of the Dart VM has a single-threaded execution model that is event-driven. In Dart terminology, this is called an isolate. The execution of a Dart program places each event (for example, a function call) into one of two queues. The VM then takes the next event and executes it.
The first queue is the main event queue, and the second is called the microtask queue. This second queue is a higher priority, but is reserved, as the name implies, for short, small tasks, such as creating a new event or setting a value:

This execution detail is hidden from the Dart developer for the most part. However, it is important to understand that execution of Dart code is highly asynchronous. The aim of this is to allow applications to continue to be responsive, while still performing longer tasks such as calculations or accessing remote resources.
The advantage of such architecture is the complete removal of the need for any locking or synchronization during program execution. Other virtual machines have to do this, and this greatly affects complexity and performance. For example, Python's GIL (Global Interpreter Lock) has been debated for years, with many alternatives suggested and experimentally implemented.
The class designer
The customers want a feature that will appeal to programmers. They would like a way for the user to quickly sketch out ideas for a class, including details such as the class name, fields, and methods.
To handle this feature, a custom dialog will be required—GenClassDialog
in custom_dialogs.dart
. This will inherit (extend
) the Dialog
class, and handle the OK button click event handler itself. Unlike the other dialogs so far in the application, this dialog must return a piece of data back to the text editor:

The input controls are one TextInputElement
and two TextAreaElements
. The placeholder
property, available in modern web browsers, will be used to guide the user on what to enter, and, as a bonus, they are convenient space savers, as we do need to add extra elements to label the inputs:
String className = name.value; String fieldsSrc = ""; String methodsSrc = ""; List<String> classFields = fields.value.split("\n"); List<String> classMethods = methods.value.split("\n"); classFields.forEach((field) => fieldsSrc += " var $field;\n"); classMethods.forEach((method) => methodsSrc += " $method(){};\n");
The text is retrieved from the controls and split by line into lists. The method forEach
is then used to traverse the list and build the source code.
The template of a simple class in the GenClassDialog
makeClass
method has placeholders for the generated source code of the fields and methods. This class code template is declared as a multi-line string using three double-quote characters. Dart supports string interpolation, which helps when putting values of variables into strings:
result = """ /// The $className Class. class $className { $fieldsSrc $className(){} $methodsSrc } """;
Variables can be embedded in strings prefixed by a $
sign. These are then replaced with the actual value of the variable when the string is used.
It is also possible to insert more complicated code, such as an objects property, "The word is ${text.length} characters long"
. Note the use of curly brackets around the expression being evaluated.
The dialog for creating the class source code is created and handled by the TextEditor
class located in the editor.dart
source file.
The sequence of events to display the dialog display is as follows:
- The
showClassGen
event handler constructs the class and puts it on screen, and the function completes, leaving the dialog visible to the user. - The user fills in the details and hits the OK button. This event triggers the
OK pressed
method on theClassGenDialog
. - The text is extracted from the fields and the source code in the
ClassGenDialog
makeClass
method. Then, theresultHandler
is called. - The
resultHandler
is thecreateClassCode
method on the editor that sets theTextAreaElement
as the constructed source code.
- Mastering NetBeans
- GraphQL學(xué)習(xí)指南
- Getting Started with ResearchKit
- Learning ELK Stack
- Unity 5.x By Example
- Python數(shù)據(jù)可視化之Matplotlib與Pyecharts實(shí)戰(zhàn)
- 組態(tài)軟件技術(shù)與應(yīng)用
- C語言程序設(shè)計(jì)
- 微服務(wù)從小白到專家:Spring Cloud和Kubernetes實(shí)戰(zhàn)
- Spring MVC+MyBatis開發(fā)從入門到項(xiàng)目實(shí)踐(超值版)
- Node.js開發(fā)指南
- Scala編程(第5版)
- Oracle數(shù)據(jù)庫編程經(jīng)典300例
- Java 從入門到項(xiàng)目實(shí)踐(超值版)
- MongoDB Cookbook(Second Edition)