Dart: Unterschied zwischen den Versionen

Aus Info-Theke
Zur Navigation springen Zur Suche springen
 
(18 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 5: Zeile 5:
* https://codingwithjoe.com/dart-fundamentals-async-await
* https://codingwithjoe.com/dart-fundamentals-async-await
* https://dartpad.dev
* https://dartpad.dev
* [[ArgParser Dart]]


= Statements =
= Statements =
<source lang=dart>for (var ix in list) {}
<syntaxhighlight lang=dart>for (var ix in list) { print(ix); }
for (var entry in list.entries) { print(entry.key + entry.value); }
for (int ix = 0; ix < 10; ix++) {}
for (int ix = 0; ix < 10; ix++) {}
while (doIt()){ }
while (doIt()){ }
Zeile 19: Zeile 21:
}
}
assert(str.isEmpty(), "String muss leer sein"); // nur im Debug aktiv.
assert(str.isEmpty(), "String muss leer sein"); // nur im Debug aktiv.
</source>
</syntaxhighlight>
== Exception ==
== Exception ==
<source lang=dart>
<syntaxhighlight lang=dart>
try {
try {
   breedMoreLlamas();
   breedMoreLlamas();
Zeile 39: Zeile 41:
   MyException(this.message);
   MyException(this.message);
}
}
</source>
</syntaxhighlight>


= Class =
= Class =
<source lang=dart>abstract class BaseLogger {
<syntaxhighlight lang=dart>abstract class BaseLogger {
   int _errors = 0;
   int _errors = 0;
   int _level;
   int _level;
Zeile 67: Zeile 69:
   const logger2 = BaseLogger.lastInstance();
   const logger2 = BaseLogger.lastInstance();
   var isSilent = logger.isSilent;
   var isSilent = logger.isSilent;
</source>
</syntaxhighlight>
 
== Named constructors ==
<syntaxhighlight lang=dart>
class Point {
  final double x;
  final double y;
  Point(this.x, this.y);
  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}
</syntaxhighlight>
 
=== Mixins ===
Mixins sind Klassen, die ihre Eigenschaften an andere Klassen ausleihen, aber nicht in die Klassenhierarchie
eingebunden sind.
 
In anderen Sprachen wird das als Trait bezeichnet.
<syntaxhighlight lang=dart>
abstract class ErrorHandler {
  final lastError = '';
  // This class is intended to be used as a mixin, and should not be extended directly.
  factory ErrorHandler._() => null;
 
  void setError(String msg) => lastError = msg;
  void print() => print('+++ $lastError');
}
class DoIt with ErrorHandler{
  ...
  bool validate(String s){
    if (s.isEmpty()){
      setError('input is empty');
    }
  }
...
}
// Mehrere Mixins:
class A extends B with Errorhandler, MyMixin {
}
</syntaxhighlight>


=== Enum ===
=== Enum ===
<source lang=dart>enum DataType { bool, int, string, customType } // Schlüsselwörter erlaubt!
<syntaxhighlight lang=dart>enum DataType { bool, int, string, customType } // Schlüsselwörter erlaubt!
DataType.values.forEach((v) => print('value: $v, index: ${v.index}'));
DataType.values.forEach((v) => print('value: $v, index: ${v.index}'));
</source>
</syntaxhighlight>


== Interface ==
== Interface ==
* Jede Klasse kann Interface sein. Dann muss jede Methode überschrieben werden
* Jede Klasse kann Interface sein. Dann muss jede Methode überschrieben werden
<source lang=dart>
<syntaxhighlight lang=dart>
class D implements A, B, C {
class D implements A, B, C {
   @override
   @override
Zeile 83: Zeile 126:
   }
   }
}
}
</source>
</syntaxhighlight>


== Generator ==
== Generator ==
<source lang=dart>Iterable<int> naturalsTo(int n) sync* {
<syntaxhighlight lang=dart>Iterable<int> naturalsTo(int n) sync* {
   int k = 0;
   int k = 0;
   while (k < n) yield k++;
   while (k < n) yield k++;
}
}
</source>
</syntaxhighlight>
* ... und mit Rekursion:
* ... und mit Rekursion:
<source lang=dart>Iterable<int> naturalsDownFrom(int n) sync* {
<syntaxhighlight lang=dart>Iterable<int> naturalsDownFrom(int n) sync* {
   if (n > 0) {
   if (n > 0) {
     yield n;
     yield n;
Zeile 98: Zeile 141:
   }
   }
}
}
</source>
</syntaxhighlight>


= Typen =
= Typen =
* Casting:
* Casting:
<source lang=dart>
<syntaxhighlight lang=dart>
final x = y as String;
final x = y as String;
</source>
</syntaxhighlight>
== typedef ==
== typedef ==
Definiert eine Methodensignatur:
Definiert eine Methodensignatur oder einen Typ-Alias:
<source lang=dart>
<syntaxhighlight lang=dart>
typedef IntList = List<int>;
typedef IntList = List<int>;
typedef Compare<T> = int Function(T a, T b);
typedef Compare<T> = int Function(T a, T b);
Zeile 115: Zeile 158:
   if (validator(input)) doIt();
   if (validator(input)) doIt();
}
}
</source>
</syntaxhighlight>


== Map ==
== Map ==
<source lang=dart>final map = <String, int>{ 'John': 1, 'Eve': 2 };
<syntaxhighlight lang=dart>final map = <String, int>{ 'John': 1, 'Eve': 2 };
final knowsEve = map.containsKey('Eve') && map.containsValue(2);
final knowsEve = map.containsKey('Eve') && map.containsValue(2);
map.remove('John');
map.remove('John');
Zeile 125: Zeile 168:
map.forEach((k, v) { print('{ key: $k, value: $v }'); });
map.forEach((k, v) { print('{ key: $k, value: $v }'); });
map.entries.forEach((e) {  print('{ key: ${e.key}, value: ${e.value} }'); });
map.entries.forEach((e) {  print('{ key: ${e.key}, value: ${e.value} }'); });
</source>
</syntaxhighlight>


== List ==
== List ==
* Indizes: start: inklusiv end: exklusiv
* Indizes: start: inklusiv end: exklusiv
<source lang=dart>final names = <String>['adam', 'bob', 'charly', 'eve'];
<syntaxhighlight lang=dart>final names = <String>['adam', 'bob', 'charly', 'eve'];
final names2 = [...names, 'judy'];
names.add('fred'); names.insert(3, 'chris');
names.add('fred'); names.insert(3, 'chris');
ix = names.indexOf('bob'); ix2 = names.indexWhere((item) => item.startsWith('b'), start);
ix = names.indexOf('bob'); ix2 = names.indexWhere((item) => item.startsWith('b'), start);
Zeile 142: Zeile 186:
names.followedBy(name2); // liefert names und die Iterables name2
names.followedBy(name2); // liefert names und die Iterables name2
names.any((item) => item.length < 3); // irgend ein Element mit der Bedingung
names.any((item) => item.length < 3); // irgend ein Element mit der Bedingung
names.every((item) => item.length < 3); // alle Elemente mit der Bedingung
allLowerThan3 = names.every((item) => item.length < 3); // alle Elemente entsprechen der Bedingung
subList = names.where((item) => item[0] > 'k');
firstNameWithLength3 = names.firstWhere((item) => item.length==3, orElse: () => '<None>');
final summary = names.fold('names:', (prevValue, item) => prevValue += ' ' + item);
final summary = names.fold('names:', (prevValue, item) => prevValue += ' ' + item);
// fold(), reduce(), shuffle(), removeWhere(),
// fold(), reduce(), shuffle(), removeWhere(),
// foreach(), join(), contains()
// foreach(), join(), contains()
</source>
</syntaxhighlight>


== DateTime ==
== DateTime ==
<source lang=dart>
<syntaxhighlight lang=dart>
// Für DateFormat
// Für DateFormat
import 'package:intl/intl.dart';
import 'package:intl/intl.dart';
Zeile 162: Zeile 208:


var linuxTime = now.millisecondsSinceEpoch ~/ 1000;
var linuxTime = now.millisecondsSinceEpoch ~/ 1000;
 
var date2 = DateTime.fromMillisecondsSinceEpoch(linuxTime * 1000);
test (date2.weekday == DateTime.monday /* 1 */ || date2.weekday == DateTime.sunday /* 7 */);
var formatter = DateFormat('yyyy.MM.dd HH:mm:ss dayOfWeek: E');
var formatter = DateFormat('yyyy.MM.dd HH:mm:ss dayOfWeek: E');
String formatted = formatter.format(now);
String formatted = formatter.format(now);
</source>
</syntaxhighlight>
 
== Set ==
<syntaxhighlight lang="dart">
var foundKeys = <String>{};
if (! foundKeys.contains('x')){
  foundKeys.add('x');
}
// Iterable unique machen:
words.split(' ').toSet().toList();
</syntaxhighlight>


== RegExpr ==
== RegExpr ==
<source lang=dart>final regExpr = RegExp(r'(\w+)\s*=\s*(\d+)');
<syntaxhighlight lang=dart>final regExpr = RegExp(r'(\w+)\s*=\s*(\d+)');
final matcher = reDisks.firstMatch(line);
      final matcher = regExpr.firstMatch('abc = 123');
 
if (matcher != null){
if (matcher != null){
   vars[matcher.group(1)] = int.parse(matcher.group(2));
   vars[matcher?.group(1)] = int.parse(matcher?.group(2) ?? 0);
}
}
</source>
</syntaxhighlight>


== String ==
== String ==
<source lang=dart>
<syntaxhighlight lang=dart>
const limit = 3;
const limit = 3;
var interpreted = "Limit: $limit Time: ${time()}";
var interpreted = "Limit: $limit Time: ${time()}";
</source>
const x = "abc" + interpreted;
</syntaxhighlight>
=== Konversion ===
=== Konversion ===
<source lang=dart>final count = int.parse("123");
<syntaxhighlight lang=dart>final count = int.parse("123");
</source>
</syntaxhighlight>


=== Formatierung ===
=== Formatierung ===
<source lang=dart>import 'package:sprintf/sprintf.dart';
<syntaxhighlight lang=dart>import 'package:sprintf/sprintf.dart';
sprintf("%02d %s", [1, "Hi"]);
sprintf("%02d %s", [1, "Hi"]);
print("${new DateTime.now().toString()}: $message\n");
print("${new DateTime.now().toString()}: $message\n");
</source>
</syntaxhighlight>
 
== Bytes ==
<syntaxhighlight lang=dart>
import 'dart:convert';
 
String foo = 'Hello world';
List<int> bytes = utf8.encode(foo);
String foo2 = utf8.decode(bytes);
</syntaxhighlight>


= Json =
= Json =
<source lang=dart>class Photo {
<syntaxhighlight lang=dart>class Photo {
   final int id;
   final int id;
   final String title;
   final String title;
Zeile 206: Zeile 274:
   }
   }
}
}
</source>
</syntaxhighlight>


= Besonderheiten =
= Besonderheiten =
* entweder optionale Positionsparameter oder optionale Namensparameter, nicht beide.
* entweder optionale Positionsparameter oder optionale Namensparameter, nicht beide.
<source lang=dart>String substr(String str, int pos, [int length, bool uppercase]){ ... }
<syntaxhighlight lang=dart>String substr(String str, int pos, [int length, bool uppercase]){ ... }
String substr2(String str, int pos, {int length, bool uppercase, Logger logger}){ ... }
String substr2(String str, int pos, {int length, bool uppercase, Logger logger}){ ... }
x = substr('Hi world', 3, 2);
x = substr('Hi world', 3, 2);
Zeile 220: Zeile 288:
x ~/ 5 // Ganzzahlige Division
x ~/ 5 // Ganzzahlige Division
logger?.log() // Aufruf von log nur, wenn logger!=null
logger?.log() // Aufruf von log nur, wenn logger!=null
</source>
</syntaxhighlight>


== Reflection, Runtime-Info ==
== Reflection, Runtime-Info ==
<source lang=dart>if (a.runtimeType == int || a.runtimeType == String){...}
<syntaxhighlight lang=dart>if (a.runtimeType == int || a.runtimeType == String){...}
if (a is String || or a is Map || a is! List){...}
if (a is String || or a is Map || a is! List){...}


</source>
</syntaxhighlight>


== UnitTest ==
== UnitTest ==
<source lang=dart>import 'import 'package:test/test.dart';
<syntaxhighlight lang=dart>import 'package:test/test.dart';
void main() {
void main() {
   group('Validators', () {
   group('Validators', () {
Zeile 240: Zeile 308:
     });
     });


</source>
</syntaxhighlight>
[https://dartdoc.takyam.com/articles/dart-unit-tests/#matchers Matcher]:
[https://dartdoc.takyam.com/articles/dart-unit-tests/#matchers Matcher]:
 
<pre>
isTrue
isTrue
equals(string)
equals(string)
Zeile 253: Zeile 321:
greaterThanOrEqualTo(v)
greaterThanOrEqualTo(v)
lessThan(v)
lessThan(v)
</pre>
== Async-Pattern ==
<syntaxhighlight lang=dart>
Future<bool> hasSubDirs(String path) async {
  var rc = false;
  final subdir = Directory(path);
  await for (var entry in subdir.list()){
    if (await Directory.isDirectory(entry.path)){
      rc = true;
      break;
    }
  }
  return rc;
}
</syntaxhighlight>

Aktuelle Version vom 29. Januar 2023, 13:18 Uhr


Links[Bearbeiten]

Statements[Bearbeiten]

for (var ix in list) { print(ix); }
for (var entry in list.entries) { print(entry.key + entry.value); }
for (int ix = 0; ix < 10; ix++) {}
while (doIt()){ }
do { } while(test());
switch(x) { 
  case '0':
  case 'A': y=3; break;
  case 'B': b=true; continue on_label_c; // Implementierung von "fall through"
  on_label_c: case 'C': y=5; break;
  default: break; 
}
assert(str.isEmpty(), "String muss leer sein"); // nur im Debug aktiv.

Exception[Bearbeiten]

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
} on Exception catch (e) {
  print('Unknown exception: $e'); // Anything else that is an exception
} catch (e, stacktrace) {
  print('Something really unknown: $e $stacktrace'); // No specified type, handles all
  rethrow; // throw the same exception again
} finally {
  cleanUp();
}
throw ArgumentError('not an int');
class MyException implements Exception{
  String message;
  MyException(this.message);
}

Class[Bearbeiten]

abstract class BaseLogger {
  int _errors = 0;
  int _level;
  static BaseLogger _lastInstance;
  bool get isSilent => _level == 0; // getter
  set isSilent(bool value) => _level = value ? 0 : 1; // setter
  BaseLogger(this._level); // constructor
  BaseLogger.silent() : this(0); // named constructor, "redirected constructor"
  // abstract function:
  void log(string message);
  void error(string message){ _errors++; log('+++ ' + message); }
  static BaseLogger lastInstance() { return _lastInstance; }
}
class Logger extends BaseLogger {
  String _filename;
  Logger(this._filename) : super(1);
  /// weiterer Constructor:
  Logger.silentLogger(this._filename): super.silent(0); // named constructor
  /// Referenz auf vorigen Construktor:
  Logger.fromConfig(String filename): this(Config(filename).getString('logFile'));
}
...
  var logger = Logger('std.log'), logger2 = Logger.silentLogger('silent.log');
  const logger2 = BaseLogger.lastInstance();
  var isSilent = logger.isSilent;

Named constructors[Bearbeiten]

class Point {
  final double x;
  final double y;
  Point(this.x, this.y);
  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

Mixins[Bearbeiten]

Mixins sind Klassen, die ihre Eigenschaften an andere Klassen ausleihen, aber nicht in die Klassenhierarchie eingebunden sind.

In anderen Sprachen wird das als Trait bezeichnet.

abstract class ErrorHandler {
  final lastError = '';
  // This class is intended to be used as a mixin, and should not be extended directly.
  factory ErrorHandler._() => null;

  void setError(String msg) => lastError = msg;
  void print() => print('+++ $lastError');
}
class DoIt with ErrorHandler{
  ...
  bool validate(String s){
    if (s.isEmpty()){
      setError('input is empty');
    }
  }
...
}
// Mehrere Mixins:
class A extends B with Errorhandler, MyMixin {
}

Enum[Bearbeiten]

enum DataType { bool, int, string, customType } // Schlüsselwörter erlaubt!
DataType.values.forEach((v) => print('value: $v, index: ${v.index}'));

Interface[Bearbeiten]

  • Jede Klasse kann Interface sein. Dann muss jede Methode überschrieben werden
class D implements A, B, C {
  @override
  void doIt(){
    // ...
  }
}

Generator[Bearbeiten]

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}
  • ... und mit Rekursion:
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

Typen[Bearbeiten]

  • Casting:
final x = y as String;

typedef[Bearbeiten]

Definiert eine Methodensignatur oder einen Typ-Alias:

typedef IntList = List<int>;
typedef Compare<T> = int Function(T a, T b);
# Alte Schreibweise:
typedef bool MyValidator(String input);
bool validate(MyValidator validator){
  if (validator(input)) doIt();
}

Map[Bearbeiten]

final map = <String, int>{ 'John': 1, 'Eve': 2 };
final knowsEve = map.containsKey('Eve') && map.containsValue(2);
map.remove('John');
map.removeWhere((k, v) => k.startsWith('J'));
final combinedMap1 = {...map1, ...map2};
map.forEach((k, v) { print('{ key: $k, value: $v }'); });
map.entries.forEach((e) {  print('{ key: ${e.key}, value: ${e.value} }'); });

List[Bearbeiten]

  • Indizes: start: inklusiv end: exklusiv
final names = <String>['adam', 'bob', 'charly', 'eve'];
final names2 = [...names, 'judy'];
names.add('fred'); names.insert(3, 'chris');
ix = names.indexOf('bob'); ix2 = names.indexWhere((item) => item.startsWith('b'), start);
names.remove('bob'); names.removeAt(2);
names2 = names.getRange(2, 3);
name3 = names.sublist(ixStart, ixEnd);
print(names.first + ' ' + names.last);
names.firstWhere( (item) => item.length == 3);
names.replaceRange(1,3, ['john', 'judith']);
final first2Elements = names.take(2);
final allButFirst2Elements = names.skip(2);
names.followedBy(name2); // liefert names und die Iterables name2
names.any((item) => item.length < 3); // irgend ein Element mit der Bedingung
allLowerThan3 = names.every((item) => item.length < 3); // alle Elemente entsprechen der Bedingung
subList = names.where((item) => item[0] > 'k');
firstNameWithLength3 = names.firstWhere((item) => item.length==3, orElse: () => '<None>');
final summary = names.fold('names:', (prevValue, item) => prevValue += ' ' + item);
// fold(), reduce(), shuffle(), removeWhere(),
// foreach(), join(), contains()

DateTime[Bearbeiten]

// Für DateFormat
import 'package:intl/intl.dart';

var now = new DateTime.now();
var moonLanding = DateTime.parse("1969-07-20 20:18:04Z");
var sixtyDaysFromNow = now.add(new Duration(days: 60));
final diffDays = sixtyDaysFromNow.difference(moonLanding).inDays;
var dateUtc = DateTime.utc(1944, 6, 6);
var local = dateUtc.toLocal();
var isBefore = now.before(sixtyDaysFromNow);

var linuxTime = now.millisecondsSinceEpoch ~/ 1000;
var date2 = DateTime.fromMillisecondsSinceEpoch(linuxTime * 1000);
test (date2.weekday == DateTime.monday /* 1 */ || date2.weekday == DateTime.sunday /* 7 */);
var formatter = DateFormat('yyyy.MM.dd HH:mm:ss dayOfWeek: E');
String formatted = formatter.format(now);

Set[Bearbeiten]

var foundKeys = <String>{};
if (! foundKeys.contains('x')){
  foundKeys.add('x');
}
// Iterable unique machen:
words.split(' ').toSet().toList();

RegExpr[Bearbeiten]

final regExpr = RegExp(r'(\w+)\s*=\s*(\d+)');
      final matcher = regExpr.firstMatch('abc = 123');

if (matcher != null){
  vars[matcher?.group(1)] = int.parse(matcher?.group(2) ?? 0);
}

String[Bearbeiten]

const limit = 3;
var interpreted = "Limit: $limit Time: ${time()}";
const x = "abc" + interpreted;

Konversion[Bearbeiten]

final count = int.parse("123");

Formatierung[Bearbeiten]

import 'package:sprintf/sprintf.dart';
sprintf("%02d %s", [1, "Hi"]);
print("${new DateTime.now().toString()}: $message\n");

Bytes[Bearbeiten]

import 'dart:convert';

String foo = 'Hello world';
List<int> bytes = utf8.encode(foo);
String foo2 = utf8.decode(bytes);

Json[Bearbeiten]

class Photo {
  final int id;
  final String title;
  Photo({this.id, this.title});
  factory Photo.fromJson(Map<String, dynamic> json) {
    return Photo(
      id: json['id'] as int,
      title: json['title'] as String
    );
  }
  final parse(){
    final jsonData = '{ "name" : "Dane", "alias" : "FilledStacks"  }';
    final parsedJson = json.decode(jsonData);
  }
}

Besonderheiten[Bearbeiten]

  • entweder optionale Positionsparameter oder optionale Namensparameter, nicht beide.
String substr(String str, int pos, [int length, bool uppercase]){ ... }
String substr2(String str, int pos, {int length, bool uppercase, Logger logger}){ ... }
x = substr('Hi world', 3, 2);
y = substr2('Hi world', 3, length:2, logger:logger);
// ..-Operator: Mehrfachzugriff auf voriges Objekt:
var person = Person()..name='Joe'..id=25;
var p = Point(10, -12)..setColor(green)..setWeight(1.22);
x ??= 5; // Zuweisung nur, wenn x==null
x ~/ 5 // Ganzzahlige Division
logger?.log() // Aufruf von log nur, wenn logger!=null

Reflection, Runtime-Info[Bearbeiten]

if (a.runtimeType == int || a.runtimeType == String){...}
if (a is String || or a is Map || a is! List){...}

UnitTest[Bearbeiten]

import 'package:test/test.dart';
void main() {
  group('Validators', () {
    test('checkEmail', () {
      expect(checkEmail('joe@example.com'), isNull);
      expect(
          checkEmail('joe@example@com'),
          equals(
              'Not an email address: joe@example@com Example: joe@example.com'));
    });

Matcher:

isTrue
equals(string)
equalsIgnoringCase(string)
startsWith(prefix)
stringContainsInOrder(List<String> substrings)
matches(regexp)
isList
isMapgreaterThan(v)
greaterThanOrEqualTo(v)
lessThan(v)

Async-Pattern[Bearbeiten]

Future<bool> hasSubDirs(String path) async {
  var rc = false;
  final subdir = Directory(path);
  await for (var entry in subdir.list()){
    if (await Directory.isDirectory(entry.path)){
      rc = true;
      break;
    }
  }
  return rc;
}