Questions et réponses d’entrevue Flutter

Online Coding Courses for Kids

Flutter est un cadre relativement nouveau pour la création d’applications multiplateformes, mais sa popularité augmente rapidement. Les employeurs reconnaissent les avantages d’une base de code unique qui leur permet de fusionner deux ou trois équipes en une seule. Le nombre d’emplois pour les développeurs Flutter est en augmentation.

Dans cet article, vous allez travailler sur une série de questions et réponses d’entretien d’embauche Flutter et Dart.

Si vous êtes un développeur à la recherche d’un nouvel emploi, répondez à chacune des questions ci-dessous. Essayez de répondre par vous-même avant de regarder la réponse. Cela peut vous aider à identifier les domaines dans lesquels vous pouvez renforcer vos compétences.

Si vous êtes ici en tant qu’employeur potentiel, parcourez les questions pour trouver des idées sur ce que vous devez demander à vos candidats.

Tout le monde – amusez-vous à tester vos propres connaissances Flutter et Dart! :]

Les questions sont réparties en trois niveaux:

  • Junior: Convient pour un développeur junior de Flutter. Vous connaissez les bases et vous avez créé quelques exemples d’applications.
  • Intermédiaire: Convient à un développeur intermédiaire ayant un vif intérêt pour le fonctionnement de Flutter et Dart. Vous avez beaucoup lu et expérimenté encore plus.
  • Sénior: Convient pour un développeur de haut niveau. C’est quelqu’un qui aime explorer en profondeur le framework Flutter et le langage Dart et qui sait gérer un projet.

À chaque niveau, vous trouverez deux types de questions:

  • Questions écrites: Convient aux tests de programmation par e-mail ou en ligne, car ils impliquent l’écriture de code.
  • Questions verbales: Bon à poser lors d’un appel vidéo ou lors d’un entretien en face à face.

Pendant que vous travaillez sur les questions, ouvrez votre IDE préféré.

Questions écrites juniors

question 1

Compte tenu de la classe suivante:

class Recipe {
  int cows;
  int trampolines;

  Recipe(this.cows, this.trampolines);
  
  int makeMilkshake() {
    return cows + trampolines;
  }
}

Convertir makeMilkshake() à un getter appelé milkshake en utilisant la syntaxe abrégée «grosse flèche».

[spoiler title=”Solution”]
Si une méthode ne contient qu’une seule ligne de code, vous pouvez réduire le nombre de lignes de code en renvoyant le résultat à l’aide du => syntaxe:

methodName(parameters) => statement;

Notez que vous n’utilisez pas le mot clé return lors de l’utilisation =>.

le makeMilkshake() la conversion serait:

int get milkshake => cows + trampolines;

[/spoiler]

question 2

Compte tenu du widget suivant:

class MyWidget extends StatelessWidget {
  final personNextToMe = 'That reminds me about the time when I was ten and our neighbor, her name was Mrs. Mable, and she said...';

  @override
  Widget build(BuildContext context) {
    return Row(children: [
      Icon(Icons.airline_seat_legroom_reduced),
      Text(personNextToMe),
      Icon(Icons.airline_seat_legroom_reduced),
    ]);
  }
}

Il y a un débordement de texte sur certains appareils étroits:

débordement de texte

Comment résoudriez-vous cela?

[spoiler title=”Solution”]

Expanded(
  child: Text(
    personNextToMe,
  ),
),

Emballer le Text widget avec un Expanded widget raconte Row ignorer le Text largeur intrinsèque du widget et attribuez-lui une largeur basée sur l’espace restant dans la ligne.

En utilisant plus d’un Expanded widget dans un Row, Column ou Flex divise uniformément l’espace entre tous les Expanded widgets. Utilisation flex pour hiérarchiser les allocations d’espace lorsqu’il y en a plus d’un Expanded widget.

Si vous avez également utilisé le Text widget overflow propriété, puis des points bonus pour vous.

En savoir plus sur contraintes de mise en page dans la documentation Flutter.
[/spoiler]

question 3

Refactorisez le code ci-dessous afin que les enfants de Row passera à la ligne suivante lorsque la largeur de l’écran est trop étroite pour eux.

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(children: [
      Chip(label: Text('I')),
      Chip(label: Text('really')),
      Chip(label: Text('really')),
      Chip(label: Text('really')),
      Chip(label: Text('really')),
      Chip(label: Text('really')),
      Chip(label: Text('really')),
      Chip(label: Text('need')),
      Chip(label: Text('a')),
      Chip(label: Text('job')),
    ]);
  }
}

[spoiler title=”Solution”]

Tout ce que vous avez à faire est de remplacer Row avec Wrap.

En savoir plus sur le Wrap widget dans l’article Medium, Widget Flutter Wrap.
[/spoiler]

Question 4

Vous avez déclaré list1 avec var, list2 avec final et list3 avec const. Quelle est la différence entre ces mots clés? Les deux dernières lignes seront-elles compilées?

var list1 = ['I', '💙', 'Flutter'];

final list2 = list1;
list2[2] = 'Dart';   // Will this line compile?
  
const list3 = list1; // Will this line compile?

[spoiler title=”Solution”]
Lors de l’utilisation du var mot-clé, le type de données est déduit et sa valeur peut changer. La ligne suivante équivaut à la première ligne ci-dessus, sauf que vous déclarez explicitement le type de données:

List list1 = ['I', '💙', 'Flutter'];

Avec final et const, vous ne pouvez pas réaffecter une nouvelle valeur après l’attribution initiale. final les valeurs sont attribuées une fois à l’exécution et un const La valeur de la variable doit être connue au moment de la compilation, définie ou codée en dur avant d’exécuter votre application.

La troisième ligne compilera. Vous ne réaffectez pas le list2 list lui-même, mais en changeant la valeur d’un élément dans la troisième position d’index (rappelez-vous, les index commencent par 0). Les listes sont modifiables par défaut dans Dart.

Si vous essayez de faire ce qui suit, cependant, il ne sera pas compilé, car vous essayez de réaffecter un final variable:

list2 = ['I', '💙', 'Dart'];

La quatrième ligne ne sera pas compilée car la valeur de list1 n’est pas attribué avant l’exécution. Lisez l’article de Dartlang, Const, statique, final, oh mon Dieu!, pour apprendre plus.
[/spoiler]

Question 5

Compte tenu de la classe suivante:

class Pizza {
  String cheese = 'cheddar';
}

Comment feriez-vous cheese privé? Comment en feriez-vous une variable globale? Quand devriez-vous utiliser les globaux?

[spoiler title=”Solution”]

Préfixer une variable avec un trait de soulignement _ le rend privé dans la bibliothèque.

class Pizza {
  String _cheese = 'cheddar';
}

Dart n’a pas le concept de variables privées de classe. Une bibliothèque est généralement un fichier et un fichier peut contenir plusieurs classes.

Si vous souhaitez créer une variable globale, déplacez-la simplement en dehors de la classe:

String cheese = 'cheddar';

Le placer en dehors de la classe en fait une variable de premier niveau, disponible partout où vous importez le fichier.

Les variables globales sont généralement mal vues car il est facile de perdre la trace de ce qui les change. Cela rend le débogage et les tests difficiles. Cependant, ils peuvent parfois être utiles, comme lorsque:

  • Hacker ensemble une démo rapide que vous n’allez pas maintenir.
  • Création de singletons pour fournir des services comme une base de données ou un authentificateur de réseau.
  • Fabrication const variables pour partager des éléments tels que les couleurs, les dimensions, les styles et les thèmes. Ces types de variables globales sont souvent stockés dans un fichier séparé, comme constantes.dart, que les bibliothèques importent ensuite.

Voir le langage Dart bibliothèque et documentation de visibilité pour plus de détails.
[/spoiler]

Questions verbales juniors

question 1

Quelle est la différence entre rechargement à chaud et redémarrage à chaud?

[spoiler title=”Solution”]

Le rechargement à chaud maintient l’état de l’application tout en mettant à jour l’interface utilisateur presque instantanément. Le redémarrage à chaud, en comparaison, prend un peu plus de temps car il réinitialise l’état de l’application à ses conditions initiales avant de mettre à jour l’interface utilisateur. Les deux sont plus rapides que le redémarrage complet, ce qui nécessite la recompilation de l’application.

Lorsque vous apportez des modifications importantes, vous devez arrêter et redémarrer l’application. En de rares occasions, vous devrez peut-être supprimer l’application de votre simulateur / émulateur ou appareil et la réinstaller.
[/spoiler]

question 2

Quelle est la différence entre StatelessWidget et StatefulWidget?

[spoiler title=”Solution”]

StatelessWidget est une classe immuable qui agit comme un modèle pour une partie de la disposition de l’interface utilisateur. Vous l’utilisez lorsque le widget ne change pas pendant l’affichage et, par conséquent, n’a pas State.

StatefulWidget est également immuable, mais il est associé à un State objet qui vous permet de reconstruire le widget avec de nouvelles valeurs à chaque appel setState(). Utilisation StatefulWidget chaque fois que l’interface utilisateur peut changer dynamiquement.

Si l’état devient plus complexe ou si le même état se trouve dans deux widgets différents, alors vous devriez envisager un solution de gestion d’état.

Vous pouvez en savoir plus sur widgets sans état et avec état dans la documentation Flutter.
[/spoiler]

question 3

Quelle est la différence entre WidgetsApp et MaterialApp?

[spoiler title=”Solution”]
WidgetsApp fournit une navigation de base. Avec la widgets bibliothèque, il comprend de nombreux widgets de base utilisés par Flutter.

MaterialApp et le correspondant material la bibliothèque est une couche construite au-dessus de WidgetsApp et le widgets bibliothèque. Il met en œuvre Conception matérielle, ce qui donne à l’application une apparence et une sensation unifiées sur n’importe quelle plate-forme ou appareil. le material bibliothèque a de nombreux widgets supplémentaires qui l’accompagnent.

Vous n’êtes certainement pas obligé d’utiliser MaterialApp dans votre projet. Vous pouvez utiliser CupertinoApp pour que les utilisateurs d’iOS se sentent chez eux, ou vous pouvez même créer votre propre ensemble de widgets personnalisés pour s’adapter à votre marque.
[/spoiler]

Question 4

Pouvez-vous nicher un Scaffold? Pourquoi ou pourquoi pas?

[spoiler title=”Solution”]
Oui, vous pouvez absolument imbriquer un Scaffold. C’est la beauté de Flutter. Vous contrôlez toute l’interface utilisateur.

Scaffold est juste un widget, vous pouvez donc le placer partout où un widget peut aller. En imbriquant un Scaffold, vous pouvez superposer les tiroirs, les barres-collations et les feuilles de fond.

Exemple d'application avec des widgets d'échafaudage imbriqués
[/spoiler]

Question 5

Quand est-il approprié d’utiliser des packages, des plugins ou des dépendances tierces?

[spoiler title=”Solution”]
Les packages et les plugins sont parfaits pour vous faire gagner du temps et du travail. Il n’est pas nécessaire de résoudre un problème complexe vous-même lorsque quelqu’un d’autre l’a déjà fait, surtout si la solution est très bien notée.

D’un autre côté, il y a aussi un risque d’être trop dépendant des packages tiers. Ils peuvent casser, avoir des bugs ou même être abandonnés. Lorsque vous aurez besoin de passer à un nouveau package plus tard, vous devrez peut-être apporter d’énormes modifications à votre code.

C’est pourquoi il est important d’isoler les packages de votre logique métier principale. Vous pouvez le faire en créant une classe Dart abstraite qui agit comme une interface pour le package. Une fois que vous avez configuré ce type d’architecture, tout ce que vous avez à faire pour changer de package est de réécrire la classe de wrapper concrète qui implémente votre interface.
[/spoiler]

Questions écrites intermédiaires

question 1

Vous créez une application de magasinage appelée RubberBaby, qui vend des poupées. Malheureusement, vous avez rencontré un problème sur la page de commande. Si un client passe une commande de poupées bleues et une autre commande de poupées rouges mais essaie ensuite de supprimer la commande de poupée bleue, la commande de poupée rouge est erronée.

Lorsque l'utilisateur supprime la commande de poupée bleue, la commande de poupée rouge prend incorrectement le montant de la commande de poupée bleue

Étant donné uniquement le code suivant, comment corrigeriez-vous les boutons du buggy RubberBaby?

class OrderPage extends StatefulWidget {
  @override
  _OrderPageState createState() => _OrderPageState();
}

class _OrderPageState extends State {
  bool isShowing = true;
  @override
  Widget build(BuildContext context) {
    return Column(children: [
      RaisedButton(
        child: (Text('Delete blue')),
        onPressed: () {
          setState(() {
            isShowing = false;
          });
        },
      ),
      if (isShowing) CounterButton(color: Colors.blue),
      CounterButton(color: Colors.red),
    ]);
  }
}

[spoiler title=”Solution”]

Lorsque vous avez un widget avec état et que quelque chose sur l’arborescence des widgets change, le framework compare les types de widgets pour voir ce qu’il peut réutiliser.

Depuis les deux CounterButton les widgets sont du même type, Flutter ne sait pas à quel widget attribuer l’état. Cela entraîne la mise à jour du bouton rouge avec l’état du compteur interne du bouton bleu.

Pour résoudre ce problème, utilisez le key propriété pour chaque widget. Cette propriété ajoute un identifiant pour le widget:

CounterButton(
  key: ValueKey('red'),
  color: Colors.red,
),

En ajoutant key, vous avez identifié de manière unique le bouton du compteur rouge et Flutter pourra conserver son état. Vous pouvez en savoir plus sur l’utilisation des clés dans l’article Medium, Clés! À quoi servent-ils?.
[/spoiler]

question 2

Emplois GitHub dispose d’une API ouverte pour interroger les postes liés au génie logiciel. L’URL suivante renvoie une liste de travaux distants:

https://jobs.github.com/positions.json?location=remote

Étant donné la classe de données simple suivante, dans laquelle vous ne vous souciez que du nom de l’entreprise et du titre du poste, écrivez une fonction qui renvoie un Future avec un List de Jobs. Vous pouvez ignorer la vérification des erreurs pour cette question.

class Job {
  Job(this.company, this.title);

  final String company;
  final String title;
}

[spoiler title=”Solution”]
Étant donné que l’API renvoie une liste de cartes JSON, l’ajout d’un fromJson constructeur à Job vous facilitera la vie:

class Job {
  Job(this.company, this.title);

  Job.fromJson(Map json)
      : company = json['company'],
        title = json['title'];

  final String company;
  final String title;
}

Il existe un certain nombre de packages que vous pouvez utiliser pour effectuer des requêtes HTTP, mais l’équipe Dart maintient les http paquet. Pour l’utiliser, ajoutez la dépendance dans pubspec.yaml:

dependencies:
  http: ^0.12.1

Vous importez le package et créez une fonction pour extraire les données des Jobs GitHub en arrière-plan:

import 'dart:convert';
import 'package:http/http.dart' as http;

Future> fetchJobs() async {
  final host = 'jobs.github.com';
  final path = 'positions.json';
  final queryParameters = {'location': 'remote'};
  final headers = {'Accept': 'application/json'};
  final uri = Uri.https(host, path, queryParameters);
  final results = await http.get(uri, headers: headers);
  final jsonList = json.decode(results.body) as List;
  return jsonList.map((job) => Job.fromJson(job)).toList();
}

Après avoir défini le Uri déclaration, vous faites la http.get request, qui renvoie une chaîne JSON.

Ensuite, en utilisant json.decode les résultats JSON sont analysés en un map, qui est converti en un List de Job objets.

Notre article, Analyser JSON dans Flutter, vous en apprendra plus sur l’utilisation d’une API Web, la création de modèles et une analyse JSON plus avancée.
[/spoiler]

question 3

Étant donné un flux Dart qui produit une série interminable de chaînes qui peuvent être soit salmon ou trout:

final fishStream = FishHatchery().stream; 
// salmon, trout, trout, salmon, ...

Transformez le flux pour qu’il renvoie la chaîne sushi uniquement pour les cinq premières instances de salmon.

[spoiler title=”Solution”]
Le flux transformé ressemble à ceci:

final fishStream = FishHatchery().stream;
final sushiStream = fishStream
    .where((fish) => fish == 'salmon')
    .map((fish) => 'sushi')
    .take(5);

Si vous souhaitez jouer davantage avec le code, voici la FishHatchery classe:

class FishHatchery {
  FishHatchery() {
    Timer.periodic(Duration(seconds: 1), 
      final isSalmon = Random().nextBool();
      final fish = (isSalmon) ? 'salmon' : 'trout';
      _controller.sink.add(fish);
    });
  }

  final _controller = StreamController();
  Stream get stream => _controller.stream;
}

Vous pouvez en savoir plus sur les flux dans la vidéo de l’équipe Flutter, Dart Streams – Flutter in Focus et dans le Dart Créer des flux docs.
[/spoiler]

Question 4

Pourquoi le code suivant bloquerait-il votre application Flutter?

String playHideAndSeekTheLongVersion() {
  var counting = 0;
  for (var i = 1; i <= 1000000000; i++) {
    counting = i;
  }
  return '$counting! Ready or not, here I come!';
}

En ferait un async aide à la fonction?

[spoiler title="Solution"]
Il bloque votre application car compter jusqu'à dix milliards est une tâche coûteuse en calcul, même pour un ordinateur.

Le code Dart s'exécute dans sa propre zone de mémoire appelée isoler - également connu sous le nom de thread de mémoire. Chaque isolat possède son propre tas de mémoire, ce qui garantit qu'aucun isolat ne peut accéder à l'état d'un autre isolat.

En faire un async La fonction n'aiderait pas non plus, car elle fonctionnerait toujours sur le même isolat.

Future playHideAndSeekTheLongVersion() async {
  var counting = 0;
  await Future(() {
    for (var i = 1; i <= 10000000000; i++) {
      counting = i;
    }
  });
  return '$counting! Ready or not, here I come!';
}

La solution est de l'exécuter sur un isolat différent:

Future makeSomeoneElseCountForMe() async {
  return await compute(playHideAndSeekTheLongVersion, 10000000000);
}

String playHideAndSeekTheLongVersion(int countTo) {
  var counting = 0;
  for (var i = 1; i <= countTo; i++) {
    counting = i;
  }
  return '$counting! Ready or not, here I come!';
}

Cela ne bloquerait pas votre interface utilisateur.

Vous pouvez en savoir plus sur les tâches asynchrones et les isolats dans la vidéo de l'équipe Flutter, Isolats et boucles d'événements - Flutter in Focus et aussi dans l'article de didierboelens.com, Futures - Isolats - Boucle d'événement.

Vous allez également recevoir une autre dose d'isolats dans la question suivante.
[/spoiler]

Questions verbales intermédiaires

question 1

Qu'est-ce que la boucle d'événements et quelle est sa relation avec les isolats?

[spoiler title="Solution"]
Dart a été l'un des premiers à adopter la distanciation sociale. Le code Dart s'exécute sur un seul thread appelé isolat. Les isolats séparés ne traînent pas ensemble - tout ce qu'ils font est de s'envoyer des SMS. En langage informatique, vous diriez que les isolats ne partagent aucune mémoire et qu'ils ne communiquent que via des messages envoyés via les ports.

Chaque isolat a une boucle d'événements, qui planifie les tâches asynchrones à exécuter. Les tâches peuvent se trouver sur l'une des deux files d'attente différentes: file d'attente de microtâche ou la file d'attente d'événements.

Les microtâches s'exécutent toujours en premier, mais ce sont principalement des tâches internes dont le développeur n'a pas à se soucier. Appeler un futur place la tâche dans la file d'attente des événements lorsque le futur s'achève.

Beaucoup de nouveaux programmeurs Dart pensent async les méthodes s'exécutent sur un thread distinct. Bien que cela puisse être vrai pour les opérations d'E / S que le système gère, ce n'est pas le cas pour votre propre code. C'est pourquoi si vous avez un calcul coûteux, vous devez l'exécuter sur un isolat séparé.

En savoir plus sur les isolats, les boucles d'événements et la concurrence dans l'article Medium, Programmation asynchrone Dart: isolements et boucles d'événements et Futures - Isolats - Boucles d'événements.
[/spoiler]

question 2

Comment réduisez-vous la reconstruction des widgets?

[spoiler title="Solution"]
Vous reconstruisez les widgets lorsque l'état change. Ceci est normal et souhaitable, car cela permet à l'utilisateur de voir les changements d'état reflétés dans l'interface utilisateur. Cependant, reconstruire des parties de l'interface utilisateur qui n'ont pas besoin d'être modifiées est un gaspillage.

Vous pouvez faire plusieurs choses pour réduire la reconstruction inutile des widgets.

  • La première consiste à refactoriser une grande arborescence de widgets en widgets individuels plus petits, chacun avec ses propres build méthode.
  • Dans la mesure du possible, utilisez le const constructeur, car cela indiquera à Flutter qu'il n'a pas besoin de reconstruire le widget.
  • Gardez le sous-arbre d'un widget avec état aussi petit que possible. Si un widget avec état a besoin d'un sous-arbre de widget, créez un widget personnalisé pour le widget avec état et donnez-lui un child paramètre.

En savoir plus sur considérations de performance dans la documentation Flutter.
[/spoiler]

question 3

Quel est BuildContext et comment est-ce utile?

[spoiler title="Solution"]
BuildContext est en fait l'élément du widget dans l'arborescence des éléments - donc chaque widget a son propre BuildContext.

Vous utilisez habituellement BuildContext pour obtenir une référence au thème ou à un autre widget. Par exemple, si vous souhaitez afficher une boîte de dialogue de matériau, vous avez besoin d'une référence à l'échafaudage. Vous pouvez l'obtenir avec Scaffold.of(context), où context est le contexte de construction. of() recherche dans l'arbre jusqu'à ce qu'il trouve l'échafaudage le plus proche.

Lire l'article de didierboelens.com, Widget - État - Contexte - Widget hérité, pour non seulement en savoir plus sur le contexte de construction, mais également sur le cycle de vie des widgets avec état et les widgets hérités.

De plus, notre article, Rendu de texte Flutter, vous emmène dans une visite de bas niveau du code source Flutter, où vous rencontrerez le contexte de construction, les éléments et même les objets de rendu.
[/spoiler]

Question 4

Comment parlez-vous au code natif depuis une application Flutter?

[spoiler title="Solution"]
Normalement, vous n'avez pas besoin de parler de code natif car le framework Flutter ou des plugins tiers le gèrent. Cependant, si vous avez besoin d'un accès spécial à la plate-forme sous-jacente, vous pouvez utiliser les canaux de la plate-forme.

Un type de canal de plate-forme est un canal de méthode. Les données sont sérialisées du côté Dart puis envoyées du côté natif. Vous pouvez écrire du code natif pour interagir avec la plate-forme avant de renvoyer un message sérialisé. Ce message peut être écrit en Java ou Kotlin sur Android ou Objective-C ou Swift sur iOS.

Cependant, vous n'utilisez pas les canaux de plate-forme sur le Web, car ils ne sont pas nécessaires.

Le deuxième type de canal de plate-forme est le canal événementiel, que vous utilisez pour renvoyer un flux de données de la plate-forme native vers Flutter. Ceci est utile pour surveiller les données des capteurs.

La documentation Flutter contient plus de détails sur canaux de plateforme.
[/spoiler]

Question 5

Quels types de tests pouvez-vous effectuer?

[spoiler title="Solution"]
Il existe trois principaux types de tests: tests unitaires, tests de widget et tests d'intégration. Les tests unitaires consistent à vérifier la validité de votre logique métier. Les tests de widget permettent de s'assurer que les widgets d'interface utilisateur ont les composants auxquels vous vous attendez. Les tests d'intégration vérifient que votre application fonctionne dans son ensemble.

Un autre type de test qui n'est pas aussi bien connu est un test d'or. Dans un test d'or, vous avez une image d'un widget ou d'un écran et vérifiez que le widget lui correspond.

En savoir plus sur les tests dans le Documents sur le livre de recettes Flutter et plus sur les tests d'or de l'article Medium, Flutter: Golden tests - Comparez les widgets avec des instantanés.

En outre, raywenderlich.com a un article sur les tests unitaires Flutter.
[/spoiler]

Questions écrites senior

question 1

Démontrez la communication Dart isoler à l'aide des ports en procédant comme suit:

  1. Donnez une fonction appelée downloadAndCompressTheInternet() à un nouvel isolat.
  2. Faites-le renvoyer la valeur 42.

[spoiler title="Solution"]

import 'dart:isolate';

void main() async {
  // 1
  final receivePort = ReceivePort();
  // 2
  final isolate = await Isolate.spawn(
    downloadAndCompressTheInternet,
    receivePort.sendPort,
  );
  // 3
  receivePort.listen((message) {
    print(message);
    receivePort.close();
    isolate.kill();
  });
}

// 4
void downloadAndCompressTheInternet(SendPort sendPort) {
  sendPort.send(42);
}

Dans ce code, vous:

  1. Créez un port pour recevoir les données du nouvel isolat.
  2. Créez un nouvel isolat, donnez-lui du travail et fournissez-lui un moyen de renvoyer des données.
  3. Écoutez tout message de données envoyé par le nouvel isolat, puis supprimez-le.
  4. Renvoyez les données en utilisant le port que l'isolat principal écoute.

L'algorithme de décompression Internet est toujours en cours de développement. :]

Lisez l'article Coding With Joe, Principes de base des fléchettes - Isolats, pour en savoir plus sur la communication isolée.
[/spoiler]

question 2

Vous avez deux structures de données arborescentes, où les entiers aléatoires sont des nœuds dans l'arborescence. Les nombres n'ont pas besoin d'être uniques et ne sont pas triés de manière logique. Les deux arbres représentent un nombre arbitraire de niveaux de profondeur. Écrivez un algorithme pour identifier les nombres du premier arbre ne pas dans la seconde.

Voici un exemple:

Diagramme montrant deux structures de données arborescentes contenant des nombres aléatoires

L'algorithme doit identifier que le nombre 1 est dans le premier arbre, mais pas dans le second.

[spoiler title="Solution"]
Définissez d'abord les nœuds dans l'arborescence:

class Node {
  int data;
  List children;

  Node(this.data, {this.children});
}

Ajoutez la logique pour rechercher l'arborescence de manière récursive, à la recherche d'entiers uniques:

class UniqueTreeItems {
  final Set _uniqueIntegers = HashSet();

  Set search(Node tree) {
    _addInOrder(tree);
    return _uniqueIntegers;
  }

  void _addInOrder(Node node) {
    _uniqueIntegers.add(node.data);
    if (node.children == null) return;
    for (final child in node.children) {
      _addInOrder(child);
    }
  }
}

Configurez les données de test:

final treeOne = Node(1, children: [
  Node(4, children: [
    Node(10),
    Node(12),
  ]),
  Node(3, children: [
    Node(3),
    Node(10),
    Node(1),
  ]),
]);

final treeTwo = Node(4, children: [
  Node(10),
  Node(3),
  Node(12),
]);

Filtrez tous les nombres entiers de l'arbre 1 qui se trouvent également dans l'arbre 2:

void main() async {
  final uniqueOne = UniqueTreeItems().search(treeOne);
  final uniqueTwo = UniqueTreeItems().search(treeTwo);
  final answer = uniqueOne.where((element) => !uniqueTwo.contains(element));
  answer.forEach(print); // 1
}

La réponse est 1.
[/spoiler]

Questions verbales pour les seniors

question 1

Quels sont les avantages et les inconvénients des différentes solutions de gestion d'état?

[spoiler title="Solution"]
Bien qu'il existe d'innombrables variétés, certaines des solutions de gestion d'état les plus populaires incluent BLoC, ChangeNotifier with Provider, Redux, MobX et RxDart. Ceux-ci sont tous appropriés pour les applications de moyenne à grande échelle; si vous ne faites qu'une application de démonstration rapide, un widget avec état suffit souvent.

Au lieu d'énumérer les avantages et les inconvénients de chaque option de gestion d'état, il est plus utile d'examiner les situations dans lesquelles une certaine classe de solutions convient mieux. Par exemple, pour quelqu'un qui est débordé par le grand nombre d'options, il est important de choisir une solution facile à comprendre mentalement. ChangeNotifier avec Provider ou MobX serait un bon choix, car il est logique d'appeler directement des méthodes sur la classe d'état en réponse à des événements.

Si vous dépendez fortement des flux, comme avec une API Firebase, alors il est naturel de choisir une solution basée sur les flux comme BLoC ou RxDart.

Et si vous avez besoin d'une fonctionnalité d'annulation / restauration, vous voudrez une solution comme BLoC ou Redux qui gère bien l'état immuable.

En fin de compte, cela dépend en grande partie de vos préférences personnelles. Vous pouvez trouver des liens vers plus d'informations sur les systèmes de gestion d'état les plus populaires dans Flutter's ' liste des approches de gestion étatique.

Il y a aussi des articles sur BLoC et Provider avec ChangeNotifier ici sur raywenderlich.com.
[/spoiler]

question 2

Comment concevriez-vous une application pour contrôler un ascenseur?

[spoiler title="Solution"]
Cette question teste vos capacités d'analyse, votre organisation et votre utilisation SOLIDE des principes.

Voici une réponse possible:

  1. Tout d'abord, déterminez quelle est la fonctionnalité de base: des choses comme ouvrir et fermer les portes, monter et descendre à différents étages, appeler à l'aide et coordonner avec d'autres ascenseurs. C'est votre logique métier. Dessiner un diagramme peut aider.
  2. Mettre en œuvre la logique métier dans Développement piloté par les tests (TDD) style. Autrement dit, écrivez un test qui échoue, écrivez juste assez de code de logique métier pour le faire réussir, refactoriser et recommencer avec un autre test.
  3. Au début, peu importe si vous avez des boutons physiques ou un écran tactile alimenté par Flutter. Peu importe à quoi ressemble l'ascenseur ou où il se trouve. Le système d'appel d'urgence n'a pas d'importance. Vous devez faire abstraction de ces facteurs externes derrière les interfaces que vous pouvez simuler pendant le développement.
  4. Une fois que vous avez terminé la logique de base, vous pouvez travailler sur l'implémentation de chacun des composants que vous représentiez auparavant uniquement avec une interface. Pour l'interface utilisateur, vous devrez configurer un système de gestion d'état qui prend en charge les événements tels que les pressions sur les boutons ou les arrivées, puis met à jour l'état, ce qui peut entraîner l'allumage d'un numéro de bouton ou la mise à jour d'un écran. Vous devrez également implémenter les services qui interagissent avec le système pour passer un appel d'urgence ou le matériel qui ouvre les portes.
  5. La sécurité est évidemment extrêmement importante pour les ascenseurs.Par conséquent, en plus de tester la logique métier principale et les différents composants du système de manière isolée, vous devrez également effectuer des tests d'intégration approfondis. Pour un ascenseur, cela impliquera des tests manuels par des robots et / ou des humains.

[/spoiler]

Où aller en partant d'ici?

Toutes nos félicitations! Vous avez atteint la fin. Ne vous sentez pas mal si vous ne connaissez pas toutes les réponses. J'ai dû faire beaucoup de recherches moi-même en les écrivant.

Considérez ceci comme un point de départ. Notez tous les domaines dans lesquels vous êtes faible, puis faites plus de recherches dans ces domaines. En lisant le Documentation Flutter et Guides de fléchettes vous apprendra beaucoup.

Si vous souhaitez en savoir plus sur Dart, consultez notre cours vidéo sur les principes de base de Dart. De plus, sur raywenderlich.com, nous publions continuellement du nouveau contenu sur tout ce qui concerne Flutter.

Si vous avez plus de suggestions de questions d'entrevue, une meilleure réponse ou même un défi de code, veuillez les laisser dans les commentaires ci-dessous.

Bonne chance dans votre recherche d'emploi!

Close Menu