Flutter & Design Patterns

Réussis tes devoirs et examens dès maintenant avec Quizwiz!

What is a design pattern?

Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customize to solve a recurring design problem in your code.

Given the following class: class Recipe { int cows; int trampolines; Recipe(this.cows, this.trampolines); int makeMilkshake() { return cows + trampolines; } } Convert makeMilkshake() to a getter called milkshake using the shorthand "fat arrow" syntax.

If a method contains only a single line of code, you can reduce the number of lines of code by returning the result using the => syntax: methodName(parameters) => statement; Note that you don't use the keyword return when using =>. The makeMilkshake() conversion would be: int get milkshake => cows + trampolines;

Git rebase vs merge

In git merge, looking at the end diagram, one can make out Commit A and B came from the feature branch. However, in the git-rebase diagram, it is difficult to understand where commits A and B came from. Therefore, we do git merge when we want our team to understand logs in a way where they can identify where each commit is coming from. We use Git Rebase when the logs of the repository will not be referred by anyone else. To summarise, we can use Git Rebase, when we are working on branches, which cannot be seen by other developers. And we use Git Merge when the target and source branch can be viewed by other developers.

When is it appropriate to use packages, plugins or third-party dependencies?

Packages and plugins are great for saving you time and work. There's no need to solve a complex problem yourself when someone else has done it already, especially if the solution is highly rated. On the other hand, there's also a danger of being too reliant on third party packages. They can break, have bugs or even be abandoned. When you need to switch to a new package down the road, you might have to make huge changes to your code. That's why it's important to isolate packages from your core business logic. You can do that by creating an abstract Dart class that acts as an interface for the package. Once you've set up that kind of architecture, all you have to do to switch packages is to rewrite the concrete wrapper class that implements your interface.

What is the difference between StatelessWidget and StatefulWidget?

StatelessWidget is an immutable class that acts as a blueprint for some part of the UI layout. You use it when the widget doesn't change while displaying and, therefore, has no State. StatefulWidget is also immutable, but it's coupled with a State object that allows you to rebuild the widget with new values whenever calling setState(). Use StatefulWidget whenever the UI can change dynamically. If the state becomes more complex or the same state is in two different widgets, then you should consider a more sophisticated state management solution. You can read more about stateless and stateful widgets in the Flutter docs.

Template Method

Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure. The Template Method pattern suggests that you break down an algorithm into a series of steps, turn these steps into methods, and put a series of calls to these methods inside a single template method. The steps may either be abstract, or have some default implementation. To use the algorithm, the client is supposed to provide its own subclass, implement all abstract steps, and override some of the optional ones if needed (but not the template method itself). Use the Template Method pattern when you want to let clients extend only particular steps of an algorithm, but not the whole algorithm or its structure. Use the pattern when you have several classes that contain almost identical algorithms with some minor differences. As a result, you might need to modify all classes when the algorithm changes. Pros You can let clients override only certain parts of a large algorithm, making them less affected by changes that happen to other parts of the algorithm. You can pull the duplicate code into a superclass. Cons Some clients may be limited by the provided skeleton of an algorithm. You might violate the Liskov Substitution Principle by suppressing a default step implementation via a subclass. Template methods tend to be harder to maintain the more steps they have.

Adapter (Structural)

Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate. Problem Imagine that you're creating a stock market monitoring app. The app downloads the stock data from multiple sources in XML format and then displays nice-looking charts and diagrams for the user. At some point, you decide to improve the app by integrating a smart 3rd-party analytics library. But there's a catch: the analytics library only works with data in JSON format. Solution You can create an adapter. This is a special object that converts the interface of one object so that another object can understand it. An adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes. The wrapped object isn't even aware of the adapter. For example, you can wrap an object that operates in meters and kilometers with an adapter that converts all of the data to imperial units such as feet and miles. Adapters can not only convert data into various formats but can also help objects with different interfaces collaborate. Here's how it works: The adapter gets an interface, compatible with one of the existing objects. Using this interface, the existing object can safely call the adapter's methods. Upon receiving a call, the adapter passes the request to the second object, but in a format and order that the second object expects. Sometimes it's even possible to create a two-way adapter that can convert the calls in both directions. Pros Single Responsibility Principle. You can separate the interface or data conversion code from the primary business logic of the program. Open/Closed Principle. You can introduce new types of adapters into the program without breaking the existing client code, as long as they work with the adapters through the client interface. Cons The overall complexity of the code increases because you need to introduce a set of new interfaces and classes. Sometimes it's simpler just to change the service class so that it matches the rest of your code. Relations with Other Patterns Bridge is usually designed up-front, letting you develop parts of an application independently of each other. On the other hand, Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely. Adapter changes the interface of an existing object, while Decorator enhances an object without changing its interface. In addition, Decorator supports recursive composition, which isn't possible when you use Adapter. Adapter provides a different interface to the wrapped object, Proxy provides it with the same interface, and Decorator provides it with an enhanced interface. Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects. Bridge, State, Strategy (and to some degree Adapter) have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn't just a recipe for structuring your code in a specific way. It can also communicate to other developers the problem the pattern solves.

Can you nest a Scaffold? Why or why not?

Yes, you can absolutely nest a Scaffold. That's the beauty of Flutter. You control the entire UI. Scaffold is just a widget, so you can put it anywhere a widget might go. By nesting a Scaffold, you can layer drawers, snack bars and bottom sheets.

What do you mean by flutter SDK?

A Flutter SDK (Software Development Kit) enables developers to build applications for mobile, web, and desktop using a single codebase. Flutter SDK includes the following features: Dart SDK Contains a rendering engine, widgets, APIs for testing and integration, etc. Compilation tools for Native Machine Code (code for iOS and Android). React-style modern framework Provide Interop and plugin APIs to connect with system and 3rd-party SDKs. A headless test runner that runs tests on Windows, Linux, and Mac. Use the Dart DevTools to test, debug, and profile your app. Use Flutter and Dart command-line tools to develop, build, test and compile your apps across platforms.

How Does Dart AOT Work? Ask Question

A compiler creates the binary code from Dart source code. For mobile applications the source code is compiled for multiple processors ARM, ARM64, x64 and for both platforms - Android and iOS. This means there are multiple resulting binary files for each supported processor and platform combination.

Bridge (Structural)

Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other. Problem Abstraction? Implementation? Sound scary? Stay calm and let's consider a simple example. Say you have a geometric Shape class with a pair of subclasses: Circle and Square. You want to extend this class hierarchy to incorporate colors, so you plan to create Red and Blue shape subclasses. However, since you already have two subclasses, you'll need to create four class combinations such as BlueCircle and RedSquare. Solution This problem occurs because we're trying to extend the shape classes in two independent dimensions: by form and by color. That's a very common issue with class inheritance. The Bridge pattern attempts to solve this problem by switching from inheritance to the object composition. What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class. Following this approach, we can extract the color-related code into its own class with two subclasses: Red and Blue. The Shape class then gets a reference field pointing to one of the color objects. Now the shape can delegate any color-related work to the linked color object. That reference will act as a bridge between the Shape and Color classes. From now on, adding new colors won't require changing the shape hierarchy, and vice versa. Use the Bridge pattern when you want to divide and organize a monolithic class that has several variants of some functionality (for example, if the class can work with various database servers). Pros You can create platform-independent classes and apps. The client code works with high-level abstractions. It isn't exposed to the platform details. Open/Closed Principle. You can introduce new abstractions and implementations independently from each other. Single Responsibility Principle. You can focus on high-level logic in the abstraction and on platform details in the implementation. Cons You might make the code more complicated by applying the pattern to a highly cohesive class. Relations with Other Patterns Bridge is usually designed up-front, letting you develop parts of an application independently of each other. On the other hand, Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely. Bridge, State, Strategy (and to some degree Adapter) have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn't just a recipe for structuring your code in a specific way. It can also communicate to other developers the problem the pattern solves. You can use Abstract Factory along with Bridge. This pairing is useful when some abstractions defined by Bridge can only work with specific implementations. In this case, Abstract Factory can encapsulate these relations and hide the complexity from the client code. You can combine Builder with Bridge: the director class plays the role of the abstraction, while different builders act as implementations.

Flyweight (Structural)

Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object. Use the Flyweight pattern only when your program must support a huge number of objects which barely fit into available RAM. Pros You can save lots of RAM, assuming your program has tons of similar objects. Cons You might be trading RAM over CPU cycles when some of the context data needs to be recalculated each time somebody calls a flyweight method. The code becomes much more complicated. New team members will always be wondering why the state of an entity was separated in such a way.

What's the difference between hot reload and hot restart?

Hot reload maintains the app state while updating the UI almost instantaneously. Hot restart, by comparison, takes a little longer because it resets the app state to its initial conditions before updating the UI. Both of these are faster than doing a full restart, which requires recompiling the app. P When making significant changes, you need to stop and restart the app. On rare occasions, you might have to delete the app from your simulator/emulator or device and reinstall it.

MVVM

MVVM architecture offers two-way data binding between view and view-model. It also helps you to automate the propagation of modifications inside View-Model to the view. The view-model makes use of observer pattern to make changes in the view-model. Model: The model stores data and related logic. It represents data that is being transferred between controller components or any other related business logic. For example, a Controller object will retrieve the student info from the school database. It manipulates data and sends it back to the database or use it to render the same data. View: The View stands for UI components like HTML, CSS, jQuery, etc. In MVVC pattern view is held responsible for displaying the data which is received from the Controller as an outcome. This View is also transformed Model (s) into the User Interface (UI). View Model: The view model is responsible for presenting functions, commands, methods, to support the state of the View. It is also accountable to operate the model and activate the events in the View. Advantages of MVVM Here, are pros/benefits of MVVM Business logic is decoupled from Ul Easy to maintain and test Easy to reuse components Loosely coupled architecture: MVVM makes your application architecture as loosely coupled. You can write unit test cases for both the viewmodel and Model layer without the need to reference the View'. Disadvantages of MVVM Here, are cons/drawback of MVVM Maintenance of lots of codes in controller Some people think that for simple UIs of MVVM architecture can be overkill. Not offers tight coupling between the view and view model Here, are cons/drawback of MVVM Maintenance of lots of codes in controller Some people think that for simple UIs of MVVM architecture can be overkill. Not offers tight coupling between the view and view model

Observer (Behavioral)

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they're observing. Use the Observer pattern when changes to the state of one object may require changing other objects, and the actual set of objects is unknown beforehand or changes dynamically. Use the pattern when some objects in your app must observe others, but only for a limited time or in specific cases. Pros Open/Closed Principle. You can introduce new subscriber classes without having to change the publisher's code (and vice versa if there's a publisher interface). You can establish relations between objects at runtime. Cons Subscribers are notified in random order.

8 time complexities that every programmer should know

O(1) - Constant time describes algorithms that take the same amount of time to compute regardless of the input size. f.e. find element in map O(log n) - Logarithmic time Logarithmic time complexities usually apply to algorithms that divide problems in half every time. For instance, let's say that we want to look for a book in a dictionary. As you know, this book has every word sorted alphabetically. If you are looking for a word, then there are at least two ways to do it: Algorithm A: Start on the first page of the book and go word by word until you find what you are looking for. Algorithm B: Open the book in the middle and check the first word on it. If the word you are looking for is alphabetically more significant, then look to the right. Otherwise, look in the left half. Divide the remainder in half again, and repeat step #2 until you find the word you are looking for. Which one is faster? The first algorithms go word by word O(n), while the algorithm B split the problem in half on each iteration O(log n). This 2nd algorithm is a binary search. O(n) - Linear time Linear running time algorithms are widespread. These algorithms imply that the program visits every element from the input. f.e: The largest item on an unsorted array O(n log n) - Linearithmic Linearithmic time complexity it's slightly slower than a linear algorithm. However, it's still much better than a quadratic algorithm. f.e: Efficient sorting algorithms like merge sort, quicksort, and others. O(n^2) - Quadratic time A function with a quadratic time complexity has a growth rate of n2. If the input is size 2, it will do four operations. If the input is size 8, it will take 64, and so on. Here are some examples of quadratic algorithms: Check if a collection has duplicated values. Sorting items in a collection using bubble sort, insertion sort, or selection sort. Find all possible ordered pairs in an array. O(n^c) - Polynomial time Polynomial running is represented as O(nc), when c > 1. As you already saw, two inner loops almost translate to O(n2) since it has to go through the array twice in most cases. Are three nested loops cubic? If each one visit all elements, then yes! Usually, we want to stay away from polynomial running times (quadratic, cubic, nc, etc.) since they take longer to compute as the input grows fast. However, they are not the worst. f.e. Tripple nested loops O(2^n) - Exponential time Exponential (base 2) running time means that the calculations performed by an algorithm double every time as the input grows. Examples of exponential runtime algorithms: Power Set: finding all the subsets on a set. Fibonacci. Travelling salesman problem using dynamic programming. O(n!) - Factorial time Factorial is the multiplication of all positive integer numbers less than itself. For instance: 5! = 5 x 4 x 3 x 2 x 1 = 120 It grows very quickly: 20! = 2,432,902,008,176,640,000 As you might guess, you want to stay away, if possible, from algorithms that have this running time! Examples of O(n!) factorial runtime algorithms: Permutations of a string. Solving the traveling salesman problem with a brute-force search

Singleton (Creational)

Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance. The Singleton pattern solves two problems at the same time, violating the Single Responsibility Principle: Ensure that a class has just a single instance. Why would anyone want to control how many instances a class has? The most common reason for this is to control access to some shared resource—for example, a database or a file. Here's how it works: imagine that you created an object, but after a while decided to create a new one. Instead of receiving a fresh object, you'll get the one you already created. Note that this behavior is impossible to implement with a regular constructor since a constructor call must always return a new object by design. Provide a global access point to that instance. Remember those global variables that you (all right, me) used to store some essential objects? While they're very handy, they're also very unsafe since any code can potentially overwrite the contents of those variables and crash the app. Just like a global variable, the Singleton pattern lets you access some object from anywhere in the program. However, it also protects that instance from being overwritten by other code. There's another side to this problem: you don't want the code that solves problem #1 to be scattered all over your program. It's much better to have it within one class, especially if the rest of your code already depends on it. Nowadays, the Singleton pattern has become so popular that people may call something a singleton even if it solves just one of the listed problems. Solution All implementations of the Singleton have these two steps in common: Make the default constructor private, to prevent other objects from using the new operator with the Singleton class. Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and saves it in a static field. All following calls to this method return the cached object. If your code has access to the Singleton class, then it's able to call the Singleton's static method. So whenever that method is called, the same object is always returned. Use the Singleton pattern when a class in your program should have just a single instance available to all clients; for example, a single database object shared by different parts of the program. Pros You can be sure that a class has only a single instance. You gain a global access point to that instance. The singleton object is initialized only when it's requested for the first time. Cons Violates the Single Responsibility Principle. The pattern solves two problems at the time. The Singleton pattern can mask bad design, for instance, when the components of the program know too much about each other. The pattern requires special treatment in a multithreaded environment so that multiple threads won't create a singleton object several times. It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects. Since the constructor of the singleton class is private and overriding static methods is impossible in most languages, you will need to think of a creative way to mock the singleton. Or just don't write the tests. Or don't use the Singleton pattern. Relations with Other Patterns A Facade class can often be transformed into a Singleton since a single facade object is sufficient in most cases. Flyweight would resemble Singleton if you somehow managed to reduce all shared states of the objects to just one flyweight object. But there are two fundamental differences between these patterns: There should be only one Singleton instance, whereas a Flyweight class can have multiple instances with different intrinsic states. The Singleton object can be mutable. Flyweight objects are immutable. Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

What is the use of Ticker in Flutter?

We use a ticker to tell how often our animation is refreshed in Flutter. Signals are sent at a constant frequency, such as 60 times per second, using this type of signal-sending class. We understand it better with our watch, which ticks constantly. For each tick, a callback method is provided that has the time since the first tick at each second since it was started. The tickers are synchronized immediately, even if they begin at different times.

How would you execute code only in debug mode?

if (Foundation.kReleaseMode){ } else { // debug mode }

What are the pros and cons of different state management solutions?

While there are countless varieties, some of the more popular state management solutions include BLoC, ChangeNotifier with Provider, Redux, MobX and RxDart. These are all appropriate for medium- to large-scale apps; if you're only making a quick demo app, then a stateful widget is often enough. Instead of listing the pros and cons of each state management option, it's more useful to look at the situations where a certain class of solutions is a better fit. For example, for someone who's overwhelmed with the sheer number of options, it's important to choose a solution that's easy to grasp, mentally. ChangeNotifier with Provider or MobX would be a good choice, because it makes sense to directly call methods on the state class in response to events. If you're heavily reliant on streams, such as with a Firebase API, then it's natural to choose a stream-based solution like BLoC or RxDart. And if you need undo/redo functionality, then you'd want a solution like BLoC or Redux that handles immutable state well. In the end, a lot of it comes down to personal preference. You can find links to more information about the most popular state management systems in Flutter's' list of state management approaches. There are also articles about BLoC and Provider with ChangeNotifier here on raywenderlich.com.

Refactor the code below so that the children of Row will wrap to the next line when the display width is too narrow for them to fit. 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')), ]); } }

All you need to do is replace Row with Wrap. Read more about the Wrap widget in the Medium article, Flutter Wrap Widget.

Command (Behavioral)

Command is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as a method arguments, delay or queue a request's execution, and support undoable operations. Use the Command pattern when you want to parametrize objects with operations. Use the Command pattern when you want to queue operations, schedule their execution, or execute them remotely. Use the Command pattern when you want to implement reversible operations. Pros Single Responsibility Principle. You can decouple classes that invoke operations from classes that perform these operations. Open/Closed Principle. You can introduce new commands into the app without breaking existing client code. You can implement undo/redo. You can implement deferred execution of operations. You can assemble a set of simple commands into a complex one. Cons The code may become more complicated since you're introducing a whole new layer between senders and receivers.

What are different build modes in flutter?

Debug Mode: This mode enables debugging of apps on a physical device, emulator, or simulator. Assertions and service extensions are enabled here. Quick deployment is then achieved by optimizing compilation. Profile Mode: In this mode, some debugging abilities are maintained, enough to analyze the app's performance while testing. Tracing and some extensions are enabled in this case. On emulators and simulators, profile mode is disabled since their behavior does not reproduce real-world performance. The following command can be used to compile the profile mode: flutter run --profile Release Mode: When deploying the app, this mode is used to minimize the size of the footprint and maximize optimization. Debugging, assertions and service extensions are disabled here. Faster startup, faster execution, and less size are its key features. The following command can be used to compile the release mode: flutter run --release

Iterator (Behavioral)

Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.). Use the Iterator pattern when your collection has a complex data structure under the hood, but you want to hide its complexity from clients (either for convenience or security reasons). Use the pattern to reduce duplication of the traversal code across your app. Use the Iterator when you want your code to be able to traverse different data structures or when types of these structures are unknown beforehand. Pros Single Responsibility Principle. You can clean up the client code and the collections by extracting bulky traversal algorithms into separate classes. Open/Closed Principle. You can implement new types of collections and iterators and pass them to existing code without breaking anything. You can iterate over the same collection in parallel because each iterator object contains its own iteration state. For the same reason, you can delay an iteration and continue it when needed. Cons Applying the pattern can be an overkill if your app only works with simple collections. Using an iterator may be less efficient than going through elements of some specialized collections directly.

Future vs Stream

A Future is like the token with a number on it that they give you when you order takeout; you made the request, but the result is not yet ready but you have a placeholder. And when the result is ready, you get a callback (the digital board above the takeout counter shows your number or they shout it out) - you can now go in and grab your food (the result) to take out. A Stream is like that belt carrying little sushi bowls. By sitting down at that table, you've "subscribed" to the stream. You don't know when the next sushi boat will arrive - but when the chef (message source) places it in the stream (belt), then the subscribers will receive it. The important thing to note is that they arrive asynchronously (you have no idea when the next boat/message will come) but they will arrive in sequence (i.e., if the chef puts three types of sushi on the belt, in some order -- you will see them come by you in that same order) From a coding perspective -- both Futures and Streams help you deal with asynchrony (where things don't happen instantly, and you don't know when you will get a result after you make a request). The difference is that Futures are about one-shot request/response (I ask, there is a delay, I get a notification that my Future is ready to collect, and I'm done!) whereas Streams are a continuous series of responses to a single request (I ask, there is a delay, then I keep getting responses until the stream dries up or I decide to close it and walk away).

Prototype (Creational)

A prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes. Instead of classes, you inherit from objects Pros You can clone objects without coupling to their concrete classes. You can get rid of repeated initialization code in favor of cloning pre-built prototypes. You can produce complex objects more conveniently. You get an alternative to inheritance when dealing with configuration presets for complex objects. Cons Cloning complex objects that have circular references might be very tricky. Relations with other paterns Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated). Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes. Prototype can help when you need to save copies of Commands into history. Designs that make heavy use of Composite and Decorator can often benefit from using Prototype. Applying the pattern lets you clone complex structures instead of re-constructing them from scratch. Prototype isn't based on inheritance, so it doesn't have its drawbacks. On the other hand, Prototype requires a complicated initialization of the cloned object. Factory Method is based on inheritance but doesn't require an initialization step. Sometimes Prototype can be a simpler alternative to Memento. This works if the object, the state of which you want to store in the history, is fairly straightforward and doesn't have links to external resources, or the links are easy to re-establish. Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

Abstract Factory (Creational)

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes. The first thing the Abstract Factory pattern suggests is to explicitly declare interfaces for each distinct product of the product family (e.g., chair, sofa or coffee table). Then you can make all variants of products follow those interfaces. For example, all chair variants can implement the Chair interface; all coffee table variants can implement the CoffeeTable interface, and so on. The next move is to declare the Abstract Factory—an interface with a list of creation methods for all products that are part of the product family (for example, createChair, createSofa and createCoffeeTable). These methods must return abstract product types represented by the interfaces we extracted previously: Chair, Sofa, CoffeeTable and so on. Now, how about the product variants? For each variant of a product family, we create a separate factory class based on the AbstractFactory interface. A factory is a class that returns products of a particular kind. For example, the ModernFurnitureFactory can only create ModernChair, ModernSofa and ModernCoffeeTable objects. The client code has to work with both factories and products via their respective abstract interfaces. This lets you change the type of a factory that you pass to the client code, as well as the product variant that the client code receives, without breaking the actual client code. Use the Abstract Factory when your code needs to work with various families of related products, but you don't want it to depend on the concrete classes of those products—they might be unknown beforehand or you simply want to allow for future extensibility. Pros You can be sure that the products you're getting from a factory are compatible with each other. You avoid tight coupling between concrete products and client code. Single Responsibility Principle. You can extract the product creation code into one place, making the code easier to support. Open/Closed Principle. You can introduce new variants of products without breaking existing client code. Cons The code may become more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern. Relations with Other Patterns Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated). Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects. Abstract Factory returns the product immediately, whereas Builder lets you run some additional construction steps before fetching the product. Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes. Abstract Factory can serve as an alternative to Facade when you only want to hide the way the subsystem objects are created from the client code. You can use Abstract Factory along with Bridge. This pairing is useful when some abstractions defined by Bridge can only work with specific implementations. In this case, Abstract Factory can encapsulate these relations and hide the complexity from the client code. Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

Explain how inherited widget works

Base class for widgets that efficiently propagate information down the tree. To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.dependOnInheritedWidgetOfExactType. Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state. The following is a skeleton of an inherited widget called FrogColor: class FrogColor extends InheritedWidget { const FrogColor({ Key? key, required this.color, required Widget child, }) : super(key: key, child: child); final Color color; static FrogColor of(BuildContext context) { final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>(); assert(result != null, 'No FrogColor found in context'); return result!; } @override bool updateShouldNotify(FrogColor old) => color != old.color; } The convention is to provide a static method of on the InheritedWidget which does the call to BuildContext.dependOnInheritedWidgetOfExactType. This allows the class to define its own fallback logic in case there isn't a widget in scope. In the example above, the value returned will be null in that case, but it could also have defaulted to a value. What is mathematical complexity ? You have immediate access wherever you are in the tree, because flutter uses maps under the hood.

What is BuildContext and how is it useful?

BuildContext is actually the widget's element in the Element tree — so every widget has its own BuildContext. You usually use BuildContext to get a reference to the theme or to another widget. For example, if you want to show a material dialog, you need a reference to the scaffold. You can get it with Scaffold.of(context), where context is the build context. of() searches up the tree until it finds the nearest scaffold. Read didierboelens.com's article, Widget — State — Context — Inherited Widget, to not only learn about the build context, but also the stateful widget life cycle and inherited widgets. Additionally, our article, Flutter Text Rendering, takes you on a low-level tour of the Flutter source code, where you'll meet build context, elements and even render objects

Builder (Creational)

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code. Pros You can construct objects step-by-step, defer construction steps or run steps recursively. You can reuse the same construction code when building various representations of products. Single Responsibility Principle. You can isolate complex construction code from the business logic of the product. Cons The overall complexity of the code increases since the pattern requires creating multiple new classes. Relations with Other Patterns Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated). Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects. Abstract Factory returns the product immediately, whereas Builder lets you run some additional construction steps before fetching the product. You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively. You can combine Builder with Bridge: the director class plays the role of the abstraction, while different builders act as implementations. Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

Chain of Responsibility (Behavioral)

Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain. Use the Chain of Responsibility pattern when your program is expected to process different kinds of requests in various ways, but the exact types of requests and their sequences are unknown beforehand. Pros You can control the order of request handling. Single Responsibility Principle. You can decouple classes that invoke operations from classes that perform operations. Open/Closed Principle. You can introduce new handlers into the app without breaking the existing client code Cons Some requests may end up unhandled.

Composite (Structural)

Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects. Problem Using the Composite pattern makes sense only when the core model of your app can be represented as a tree. For example, imagine that you have two types of objects: Products and Boxes. A Box can contain several Products as well as a number of smaller Boxes. These little Boxes can also hold some Products or even smaller Boxes, and so on. Say you decide to create an ordering system that uses these classes. Orders could contain simple products without any wrapping, as well as boxes stuffed with products...and other boxes. How would you determine the total price of such an order? You could try the direct approach: unwrap all the boxes, go over all the products and then calculate the total. That would be doable in the real world; but in a program, it's not as simple as running a loop. You have to know the classes of Products and Boxes you're going through, the nesting level of the boxes and other nasty details beforehand. All of this makes the direct approach either too awkward or even impossible. Solution The Composite pattern suggests that you work with Products and Boxes through a common interface which declares a method for calculating the total price. How would this method work? For a product, it'd simply return the product's price. For a box, it'd go over each item the box contains, ask its price and then return a total for this box. If one of these items were a smaller box, that box would also start going over its contents and so on, until the prices of all inner components were calculated. A box could even add some extra cost to the final price, such as packaging cost. The greatest benefit of this approach is that you don't need to care about the concrete classes of objects that compose the tree. You don't need to know whether an object is a simple product or a sophisticated box. You can treat them all the same via the common interface. When you call a method, the objects themselves pass the request down the tree. Use the Composite pattern when you have to implement a tree-like object structure. Pros You can work with complex tree structures more conveniently: use polymorphism and recursion to your advantage. Open/Closed Principle. You can introduce new element types into the app without breaking the existing code, which now works with the object tree. Cons It might be difficult to provide a common interface for classes whose functionality differs too much. In certain scenarios, you'd need to overgeneralize the component interface, making it harder to comprehend.

What is the event loop, and what is its relationship to isolates?

Dart was an early adopter of social distancing. Dart code runs on a single thread called an isolate. Separate isolates don't hang out together — the most they do is text each other. In computer-speak, you'd say that isolates don't share any memory and they only communicate through messages sent over ports. Every isolate has an event loop, which schedules asynchronous tasks to run. The tasks can be on one of two different queues: the microtask queue or the event queue. Microtasks always run first, but they are mainly internal tasks that the developer doesn't need to worry about. Calling a future puts the task on the event queue when the future completes. A lot of new Dart programmers think async methods run on a separate thread. Although that may be true for I/O operations that the system handles, it isn't the case for your own code. That's why if you have an expensive computation, you need to run it on a separate isolate. Read more about isolates, event loops, and concurrency in the Medium article, Dart asynchronous programming: Isolates and event loops and Futures — Isolates — Event Loops.

Decorator (Structural)

Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors. if (enabledEncryption) source = new EncryptionDecorator(source) if (enabledCompression) source = new CompressionDecorator(source) Extending a class is the first thing that comes to mind when you need to alter an object's behavior. However, inheritance has several serious caveats that you need to be aware of. Inheritance is static. You can't alter the behavior of an existing object at runtime. You can only replace the whole object with another one that's created from a different subclass. Subclasses can have just one parent class. In most languages, inheritance doesn't let a class inherit behaviors of multiple classes at the same time. One of the ways to overcome these caveats is by using Aggregation or Composition instead of Inheritance. Both of the alternatives work almost the same way: one object has a reference to another and delegates it some work, whereas with inheritance, the object itself is able to do that work, inheriting the behavior from its superclass. With this new approach you can easily substitute the linked "helper" object with another, changing the behavior of the container at runtime. An object can use the behavior of various classes, having references to multiple objects and delegating them all kinds of work. Aggregation/composition is the key principle behind many design patterns, including Decorator. On that note, let's return to the pattern discussion. Use the Decorator pattern when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects. Use the pattern when it's awkward or not possible to extend an object's behavior using inheritance. Pros You can extend an object's behavior without making a new subclass. You can add or remove responsibilities from an object at runtime. You can combine several behaviors by wrapping an object into multiple decorators. Single Responsibility Principle. You can divide a monolithic class that implements many possible variants of behavior into several smaller classes. Cons It's hard to remove a specific wrapper from the wrappers stack. It's hard to implement a decorator in such a way that its behavior doesn't depend on the order in the decorators stack. The initial configuration code of layers might look pretty ugly.

How do we classify design patterns?

Design patterns differ by their complexity, level of detail and scale of applicability to the entire system being designed. I like the analogy to road construction: you can make an intersection safer by either installing some traffic lights or building an entire multi-level interchange with underground passages for pedestrians. The most basic and low-level patterns are often called idioms. They usually apply only to a single programming language. The most universal and high-level patterns are architectural patterns. Developers can implement these patterns in virtually any language. Unlike other patterns, they can be used to design the architecture of an entire application. In addition, all patterns can be categorized by their intent, or purpose. This book covers three main groups of patterns: Creational patterns provide object creation mechanisms that increase flexibility and reuse of existing code. Structural patterns explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient. Behavioral patterns take care of effective communication and the assignment of responsibilities between objects.

Given the following widget: 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), ]); } } There is a text overflow on some narrow devices(overflows) How would you fix this?

Expanded( child: Text( personNextToMe, ), ), Wrapping the Text widget with an Expanded widget tells Row to ignore the Text widget's intrinsic width and assign it a width based on the remaining space in the row. Using more than one Expanded widget in a Row, Column or Flex evenly splits the space among all the Expanded widgets. Use flex to prioritize space allocations when there's more than one Expanded widget. If you also used the Text widget's overflow property, then bonus points for you. Read more about layout constraints in the Flutter docs.

Facade (Structural)

Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes. A facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. A facade might provide limited functionality in comparison to working with the subsystem directly. However, it includes only those features that clients really care about. Having a facade is handy when you need to integrate your app with a sophisticated library that has dozens of features, but you just need a tiny bit of its functionality. For instance, an app that uploads short funny videos with cats to social media could potentially use a professional video conversion library. However, all that it really needs is a class with the single method encode(filename, format). After creating such a class and connecting it with the video conversion library, you'll have your first facade. Use the Facade pattern when you need to have a limited but straightforward interface to a complex subsystem. Pros You can isolate your code from the complexity of a subsystem. Cons A facade can become a god object coupled to all classes of an app.

Factory Method (Creational)

Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Don't worry: the objects are still created via the new operator, but it's being called from within the factory method. Objects returned by a factory method are often referred to as products. Pros You avoid tight coupling between the creator and the concrete products. Single Responsibility Principle. You can move the product creation code into one place in the program, making the code easier to support. Open/Closed Principle. You can introduce new types of products into the program without breaking existing client code. Cons The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best case scenario is when you're introducing the pattern into an existing hierarchy of creator classes. Connection Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated). Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes. You can use Factory Method along with Iterator to let collection subclasses return different types of iterators that are compatible with the collections. Prototype isn't based on inheritance, so it doesn't have its drawbacks. On the other hand, Prototype requires a complicated initialization of the cloned object. Factory Method is based on inheritance but doesn't require an initialization step. Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method.

You have two tree data structures, where random integers are nodes in the tree. The numbers don't have to be unique, nor are they sorted in any logical way. Both trees are an arbitrary number of levels deep. Write an algorithm to identify any numbers in the first tree that are not in the second. Here's an example: (in the anawer) Diagram showing two tree data structures containing random numbers The algorithm should identify that the number 1 is in the first tree, but not in the second.

First define the nodes in the tree: class Node { int data; List<Node> children; Node(this.data, {this.children}); } Add the logic to search the tree recursively, looking for unique integers: class UniqueTreeItems { final Set<int> _uniqueIntegers = HashSet<int>(); Set<int> 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); } } } Set up the test data: 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), ]); Filter out any integers from Tree 1 that are also in Tree 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 } The answer is 1.

What kind of trees do we have in Flutter and why?

Flutter framework has 3 different trees that are responsible for app rendering and different things under the hood! The 3 trees that make the Flutter framework beautiful and strong are: Widget Tree Element Tree Render Object Tree All these trees have different responsibilities but work on a common goal: Fast UI rendering and development! Let's see them one by one. Widget Tree Those who are working in Flutter would already know what Widget is. But, if you are completely new, Widget can be considered as the smallest UI component that you can use to show on the device screen. Now, the widget tree is a tree that will be created based on how you design your screen and how you are adding widgets to your screen. So you can consider the Widget Tree as the most simple tree in Flutter! Let's say you have a Center() widget and inside it, you have a Text() widget as a child so your Widget tree will have Center as root and Text as its child. Element Tree For each widget that you create in Widget Tree, the Flutter framework will create an element into Element Tree using the createElement() method. An Element represents the use of a widget to configure a specific location in the tree. This element will depict whether the widget is a Stateless Element or a Stateful Element. The element can change if the parent widget rebuilds and creates a new widget for this location. So let's say you have an AppBar widget, then it is a Stateless Element so Flutter will create a Stateless Element for the AppBar widget. Render Object Tree RenderObject is the one that is visible on the screen. For each element in the Element Tree, the Flutter framework creates a RenderObject using the createRenderObject() method on a Widget. RenderObjects store basic details of widgets like child position, basic layout, paint protocols, etc. However, it doesn't define how many children a parent will have or where the child will be positioned in the cartesian system. Hence, when the Flutter framework renders or draws your UI, it looks at the Render Object Tree instead of Widget or Element Tree because Render Tree is the one who controls all the properties of the widget! This is the reason why re-instantiating the Render Object Tree is heavy and costlier! Conclusion Widget Tree contains all the widgets that you use in Flutter and holds the configuration of a UI! Element tree represents the use of a widget to configure a specific location in the tree and contains a piece of UI (Stateless element or Stateful element)! Render Object Tree or Render Tree holds the properties of the widget along with paint protocols and is responsible for painting what you view on the screen!

Why would the following code block your Flutter app? String playHideAndSeekTheLongVersion() { var counting = 0; for (var i = 1; i <= 1000000000; i++) { counting = i; } return '$counting! Ready or not, here I come!'; } Would making it an async function help?

It blocks your app because counting to ten billion is a computationally expensive task, even for a computer. Dart code runs inside its own area of memory called an isolate — also known as memory thread. Each isolate has its own memory heap, which ensures that no isolate can access any other isolate's state. Making it an async function wouldn't help, either, because it would still run on the same isolate. Future<String> playHideAndSeekTheLongVersion() async { var counting = 0; await Future(() { for (var i = 1; i <= 10000000000; i++) { counting = i; } }); return '$counting! Ready or not, here I come!'; } The solution is to run it on a different isolate: Future<String> 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!'; } This would not block your UI. You can read more about asynchronous tasks and isolates in the Flutter team's video, Isolates and Event Loops — Flutter in Focus and also in didierboelens.com's article, Futures — Isolates — Event Loop. You're also going to get another dose of isolates in the next question.

What do you mean by keys in flutter? When one should use it.

Keys are used in Flutter as identifiers for widgets, elements, and semantic nodes. GlobalKeys and LocalKeys are the subclasses of Key. Within the widget tree, keys are responsible for preserving the state of modified widgets. With keys, you can also reorganize and modify collections of widgets that have an equivalent type and defined state. The primary use of keys is to modify a widget tree that contains stateful widgets, not to modify a tree that is totally composed of stateless widgets.

MVS vs MVVM

MVC framework is an architectural pattern that separates an applications into three main logical components Model, View, and Controller. On the other hand MVVM facilitates a separation of development of the graphical user interface with the help of mark-up language or GUI code. In MVC, controller is the entry point to the Application, while in MVVM, the view is the entry point to the Application. MVC Model component can be tested separately from the user, while MVVM is easy for separate unit testing, and code is event-driven. MVC architecture has "one to many" relationships between Controller & View while in MVVC architecture, "one to many" relationships between View & View Model.

Mediator (Behavioral)

Mediator is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object. Use the Mediator pattern when it's hard to change some of the classes because they are tightly coupled to a bunch of other classes. Use the pattern when you can't reuse a component in a different program because it's too dependent on other components. Use the Mediator when you find yourself creating tons of component subclasses just to reuse some basic behavior in various contexts. Pros Single Responsibility Principle. You can extract the communications between various components into a single place, making it easier to comprehend and maintain. Open/Closed Principle. You can introduce new mediators without having to change the actual components. You can reduce coupling between various components of a program. You can reuse individual components more easily. Cons Over time a mediator can evolve into a God Object.

Memento (Behavioral)

Memento is a behavioral design pattern that lets you save and restore the previous state of an object without revealing the details of its implementation. Use the Memento pattern when you want to produce snapshots of the object's state to be able to restore a previous state of the object. Use the pattern when direct access to the object's fields/getters/setters violates its encapsulation. Pros You can produce snapshots of the object's state without violating its encapsulation. You can simplify the originator's code by letting the caretaker maintain the history of the originator's state. Cons The app might consume lots of RAM if clients create mementos too often. Caretakers should track the originator's lifecycle to be able to destroy obsolete mementos. Most dynamic programming languages, such as PHP, Python and JavaScript, can't guarantee that the state within the memento stays untouched.

How do you talk to native code from within a Flutter app?

Normally you don't need to talk to native code because the Flutter framework or third party plugins handle it. However, if you do find yourself needing to get special access to the underlying platform, you can use platform channels. One type of platform channel is a method channel. Data is serialized on the Dart side and then sent to the native side. You can write native code to interact with the platform before sending a serialized message back. That message might be written in Java or Kotlin on Android or Objective-C or Swift on iOS. You don't use platform channels on the web, however, because they're an unnecessary step. The second type of platform channel is the event channel, which you use to send a stream of data from the native platform back to Flutter. This is useful for monitoring sensor data. The Flutter docs have more details about platform channels.

Given the following class: class Pizza { String cheese = 'cheddar'; } How would you make cheese private? How would you make it a global variable? When should you use globals?

Prefixing a variable with an underscore _ makes it private within the library. class Pizza { String _cheese = 'cheddar'; } Dart doesn't have the concept of class private variables. A library is generally a file and a file can contain multiple classes. If you want to make a global variable, just move it outside of the class: String cheese = 'cheddar'; Putting it outside the class makes it a top-level variable, which is available anywhere you import the file. Global variables are generally frowned upon because it's easy to lose track of what's changing them. This makes debugging and testing difficult. However, they can be useful sometimes, like when: Hacking together a quick demo that you aren't going to maintain. Creating Singletons to provide services like a database or network authenticator. Making const variables to share things like colors, dimensions, styles and themes. These types of global variables are often stored in a separate file, like constants.dart, which the libraries then import. See the Dart language's library and visibility documentation for more details.

Proxy (Structural)

Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object. Applicability Lazy initialization (virtual proxy). This is when you have a heavyweight service object that wastes system resources by being always up, even though you only need it from time to time. Access control (protection proxy). This is when you want only specific clients to be able to use the service object; for instance, when your objects are crucial parts of an operating system and clients are various launched applications (including malicious ones). Local execution of a remote service (remote proxy). This is when the service object is located on a remote server. Logging requests (logging proxy). This is when you want to keep a history of requests to the service object. Caching request results (caching proxy). This is when you need to cache results of client requests and manage the life cycle of this cache, especially if results are quite large. Smart reference. This is when you need to be able to dismiss a heavyweight object once there are no clients that use it. Pros You can control the service object without clients knowing about it. You can manage the lifecycle of the service object when clients don't care about it. The proxy works even if the service object isn't ready or is not available. Open/Closed Principle. You can introduce new proxies without changing the service or clients. Cons The code may become more complicated since you need to introduce a lot of new classes. The response from the service might get delayed.

GitHub Jobs has an open API for querying software engineering-related job positions. The following URL returns a list of remote jobs: https://jobs.github.com/positions.json?location=remote Given the following simple data class, in which you only care about the company name and job title, write a function that returns a Future with a List of Jobs. You can ignore error checking for this question. class Job { Job(this.company, this.title); final String company; final String title; }

Since the API returns a list of JSON maps, adding a fromJson constructor to Job will make your life easier: class Job { Job(this.company, this.title); Job.fromJson(Map<String, dynamic> json) : company = json['company'], title = json['title']; final String company; final String title; } There are a number of packages that you could use to make HTTP requests, but the Dart team maintains the basic http package. To use it, add the dependency in pubspec.yaml: dependencies: http: ^0.12.1 You import the package and create a function to pull the data from GitHub Jobs in the background: import 'dart:convert'; import 'package:http/http.dart' as http; Future<List<Job>> 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(); } After defining the Uri statement, you make the http.get request, which returns a JSON string. Next, using json.decode the JSON results are parsed into a map, which is converted to a List of Job objects. Our article, Parsing JSON in Flutter, will teach you more about using a web API, making models and more advanced JSON parsing.

State (Behavioral)

State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class. The State pattern suggests that you create new classes for all possible states of an object and extract all state-specific behaviors into these classes. Use the State pattern when you have an object that behaves differently depending on its current state, the number of states is enormous, and the state-specific code changes frequently. Use the pattern when you have a class polluted with massive conditionals that alter how the class behaves according to the current values of the class's fields. Use State when you have a lot of duplicate code across similar states and transitions of a condition-based state machine. Pros Single Responsibility Principle. Organize the code related to particular states into separate classes. Open/Closed Principle. Introduce new states without changing existing state classes or the context. Simplify the code of the context by eliminating bulky state machine conditionals. Cons Applying the pattern can be overkill if a state machine has only a few states or rarely changes.

Strategy (Behavioral)

Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable. Imagine that you have to get to the airport. You can catch a bus, order a cab, or get on your bicycle. These are your transportation strategies. You can pick one of the strategies depending on factors such as budget or time constraints. Use the Strategy pattern when you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime. Use the Strategy when you have a lot of similar classes that only differ in the way they execute some behavior. Use the pattern to isolate the business logic of a class from the implementation details of algorithms that may not be as important in the context of that logic. Use the pattern when your class has a massive conditional operator that switches between different variants of the same algorithm. Pros You can swap algorithms used inside an object at runtime. You can isolate the implementation details of an algorithm from the code that uses it. You can replace inheritance with composition. Open/Closed Principle. You can introduce new strategies without having to change the context. Cons If you only have a couple of algorithms and they rarely change, there's no real reason to overcomplicate the program with new classes and interfaces that come along with the pattern. Clients must be aware of the differences between strategies to be able to select a proper one. A lot of modern programming languages have functional type support that lets you implement different versions of an algorithm inside a set of anonymous functions. Then you could use these functions exactly as you'd have used the strategy objects, but without bloating your code with extra classes and interfaces.

IoC and Dependency Injection

The Inversion-of-Control (IoC) pattern, is about providing any kind of callback (which controls reaction), instead of acting ourself directly (in other words, inversion and/or redirecting control to external handler/controller). The Dependency-Injection (DI) pattern is a more specific version of IoC pattern, and is all about removing dependencies from your code. Every DI implementation can be considered IoC, but one should not call it IoC, because implementing Dependency-Injection is harder than callback (Don't lower your product's worth by using general term "IoC" instead). For DI example, say your application has a text-editor component, and you want to provide spell checking. Your standard code would look something like this: public class TextEditor { private SpellChecker checker; public TextEditor() { this.checker = new SpellChecker(); } } What we've done here creates a dependency between the TextEditor and the SpellChecker. In an IoC scenario we would instead do something like this: public class TextEditor { private IocSpellChecker checker; public TextEditor(IocSpellChecker checker) { this.checker = checker; } } In the first code example we are instantiating SpellChecker (this.checker = new SpellChecker();), which means the TextEditor class directly depends on the SpellChecker class. In the second code example we are creating an abstraction by having the SpellChecker dependency class in TextEditor's constructor signature (not initializing dependency in class). This allows us to call the dependency then pass it to the TextEditor class like so: SpellChecker sc = new SpellChecker(); // dependency TextEditor textEditor = new TextEditor(sc); Now the client creating the TextEditor class has control over which SpellChecker implementation to use because we're injecting the dependency into the TextEditor signature.

MVC

The MVC framework is an architectural pattern that separates an applications into three main logical components Model, View, and Controller. Hence the abbreviation MVC. The full form MVC is Model View Controller. Three important MVC the components are: Model: It includes all the data and its related logic. View: Present data to the user or handles user interaction. Controller: An interface between Model and View components. Model The model component stores data and related logic. It represents data that is being transferred between controller components or any other related business logic. For example, a Controller object helps you to retrieve the customer info from the database. It manipulates data and sends it back to the database or use it to render the same data. View A View is that part of the Application that represents the presentation of data. Views are created by the data gathered from the model data. A view requests the Model to give information so that it resents the output to the user. The View also represents the data from charts, diagrams, and table. For example, any customer view will include all the UI components like text boxes, dropdowns, etc. Controller The Controller is that part of the Application that handles the user interaction. The Controller interprets the mouse and keyboard inputs from the user, informing the Model and the View to change as appropriate. A Controller sends commands to the Model to update its state(E.g., Saving a specific document). The Controller also sends commands to its associated view to change the View's presentation (For example, scrolling a particular document). Advantages of MVC Here, are advantages/pros of MVC Easier support for a new type of clients The development of the various components can be performed parallelly. It avoids complexity by dividing an application into separate (MVC) units It only uses a front controller pattern that processes web application requests using a single controller. Offers the best support for test-driven development It works well for Web apps, which are supported by large teams of web designers and developers. It provides a clean separation of concerns(SoC). All classed and objects are independent of each other so that you can test them separately. MVC allows logical grouping of related actions on a controller together. Disadvantages of MVC Here, are cons/drawback of MVC Business logic is mixed with Ul Hard to reuse and implement tests No formal validation support Increased complexity and Inefficiency of data The difficulty of using MVC with the modern user interface There is a need for multiple programmers to conduct parallel programming. Knowledge of multiple technologies is required.

Whats the difference between double.infinity and MediaQuery?

The difference can be summarized into: I want to be as big as my parent allows (double.infinity) I want to be as big as the screen (MediaQuery). Usually, you'll want to use double.infinity, but it's not always possible. Some Widgets allow their children to be as big as they want to be (Column, ListView, OverflowBox...). In that situation using double.infinity creates a paradox: The parent allows any size The child wants the biggest size allowed by the parent

What do you understand about tween animation?

The shortened version of in-between animation is tween animation. The start and endpoints of an animation must be specified in tween animation. Using this method, the animation can begin at the beginning and can progress through a series of values until it reaches the endpoint. Transition speed and duration are also determined by using the tween animation. Calculating the transition from the beginning to the end will be easier with the widget framework.

Given a Dart stream that produces an unending series of strings that can be either salmon or trout: final fishStream = FishHatchery().stream; // salmon, trout, trout, salmon, ... Transform the stream so it returns the string sushi only for the first five instances of salmon

The transformed stream looks like this: final fishStream = FishHatchery().stream; final sushiStream = fishStream .where((fish) => fish == 'salmon') .map((fish) => 'sushi') .take(5); If you'd like to play with the code more, here's the FishHatchery class: class FishHatchery { FishHatchery() { Timer.periodic(Duration(seconds: 1), (t) { final isSalmon = Random().nextBool(); final fish = (isSalmon) ? 'salmon' : 'trout'; _controller.sink.add(fish); }); } final _controller = StreamController<String>(); Stream<String> get stream => _controller.stream; } You can learn more about streams in the Flutter's team video, Dart Streams — Flutter in Focus and in the Dart Creating Streams docs.

What types of tests can you perform?

There are three main kinds of tests: unit tests, widget tests and integration tests. Unit tests are all about checking the validity of your business logic. Widget tests are for making sure UI widgets have the components that you expect them to. Integration tests check that your app is working as a whole. One additional type of test that is not as well known is a golden test. In a golden test, you have an image of a widget or screen and check to see that the actual widget matches it. Learn more about testing in the Flutter Cookbook docs and more on golden tests from the Medium article, Flutter: Golden tests — compare Widgets with Snapshots. Also, raywenderlich.com has an article about Flutter unit testing.

What are the limitations of Flutter?

Third-party libraries are limited: Since Flutter is relatively new, the number of third-party libraries is small. Additionally, some widgets in Flutter are only available on one platform. Release size is larger: Developers get frustrated when the release size is not as expected. Requirements of Dart: Dart is an Object-oriented programming language, but it cannot compete with Java, JavaScript, or C# since it is still relatively new. As a result, not many developers choose it. Limited complexity: Flutter's 3D modeling, Unity integration, and game engines fall short. Therefore, most ad mobile platforms also don't support it. Lack of overall support: Flutter is not so widely used yet. Even though it enjoys the attention of tech enthusiasts, it still lacks the continuous support that will come with time. Currently, the only support that Flutter receives comes from its community.

How would you design an app to control an elevator?

This question tests your analytical skills, organization and use of SOLID principles. Here's one possible answer: First, determine what the core functionality is: things like opening and closing the doors, moving up and down to different floors, calling for help and coordinating with other elevators. This is your business logic. Drawing a diagram may help. Implement the business logic in Test Driven Development (TDD) style. That is, write a failing test, write just enough business logic code to make it pass, refactor and then do it all again with another test. In the beginning, it doesn't matter if you have physical buttons or a Flutter-powered touch screen. It doesn't matter what the elevator looks like or where it is. It doesn't matter what the emergency call system is. You should abstract these external factors behind interfaces that you can mock out during development. Once you've completed the core logic, you can work on implementing each of the components that you previously only represented with an interface. For the UI, you'll need to set up a state management system that takes in events like button pushes or arrivals and then updates the state, which could result in lighting a button number or updating a screen. You'll also need to implement the services that interact with the system for making an emergency call or the hardware that opens the doors. Safety is obviously extremely important for elevators, so in addition to testing the core business logic and the various system components in isolation, you'll also need to do thorough integration testing. For an elevator, that will involve manual testing by robots and/or humans.

What is the difference between WidgetsApp and MaterialApp?

WidgetsApp provides basic navigation. Together with the widgets library, it includes many of the foundational widgets that Flutter uses. MaterialApp and the corresponding material library is a layer built on top of WidgetsApp and the widgets library. It implements Material Design, which gives the app a unified look and feel on any platform or device. The material library has many additional widgets that come with it. You certainly aren't required to use MaterialApp in your project. You can use CupertinoApp to make iOS users feel at home, or you can even build your own set of custom widgets to fit your brand.

How do you reduce widget rebuild?

You rebuild widgets when the state changes. This is normal and desirable, because it allows the user to see the state changes reflected in the UI. However, rebuilding parts of the UI that don't need to change is wasteful. There are several things you can do to reduce unnecessary widget rebuilding. The first is to refactor a large widget tree into smaller individual widgets, each with its own build method. Whenever possible, use the const constructor, because this will tell Flutter that it doesn't need to rebuild the widget. Keep the subtree of a stateful widget as small as possible. If a stateful widget needs to have a widget subtree under it, create a custom widget for the stateful widget and give it a child parameter. Read more about performance considerations in the Flutter docs.

Visitor

Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which they operate. The Visitor pattern suggests that you place the new behavior into a separate class called visitor, instead of trying to integrate it into existing classes. The original object that had to perform the behavior is now passed to one of the visitor's methods as an argument, providing the method access to all necessary data contained within the object. Use the Visitor when you need to perform an operation on all elements of a complex object structure (for example, an object tree). Use the Visitor to clean up the business logic of auxiliary behaviors. Use the pattern when a behavior makes sense only in some classes of a class hierarchy, but not in others. Props Open/Closed Principle. You can introduce a new behavior that can work with objects of different classes without changing these classes. Single Responsibility Principle. You can move multiple versions of the same behavior into the same class. A visitor object can accumulate some useful information while working with various objects. This might be handy when you want to traverse some complex object structure, such as an object tree, and apply the visitor to each object of this structure. Cons You need to update all visitors each time a class gets added to or removed from the element hierarchy. Visitors might lack the necessary access to the private fields and methods of the elements that they're supposed to work with.

You've declared list1 with var, list2 with final and list3 with const. What's the difference between these keywords? Will the last two lines compile? var list1 = ['I', '💙', 'Flutter']; final list2 = list1; list2[2] = 'Dart'; // Will this line compile? const list3 = list1; // Will this line compile?

When using the var keyword, the Data type is inferred and its value can change. The following line is equivalent to the first line above, except that you explicitly declare the data type: List<String> list1 = ['I', '💙', 'Flutter']; With final and const, you can't reassign a new value after the initial assignment. final values are assigned once at runtime and a const variable value has to be either known at compile time, set, or hard coded before you run your app. The third line will compile. You're not reassigning the list2 list itself, but changing the value of an item in the third index position (remember, indexes start with 0). Lists are mutable by default in Dart. If you tried to do the following, though, it wouldn't compile because you're trying to reassign a final variable: list2 = ['I', '💙', 'Dart']; The fourth line will not compile because the value of list1 isn't assigned until runtime. Read Dartlang's article, Const, Static, Final, Oh my!, to learn more.

You're making a shopping app called RubberBaby, which sells dolls. Unfortunately, you've run into a problem on the order page. If a customer makes one order for blue dolls and another order for red dolls but then tries to delete the blue doll order, the red doll order is wrong. Given only the following code, how would you fix the RubberBaby buggy buttons? class OrderPage extends StatefulWidget { @override _OrderPageState createState() => _OrderPageState(); } class _OrderPageState extends State<OrderPage> { 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), ]); } }

When you have a stateful widget and something about the widget tree changes, the framework compares widget types to see what it can reuse. Since both CounterButton widgets are of the same type, Flutter doesn't know which widget to assign the state to. That results in the red button updating with the blue button's internal counter state. To address this, use the key property for each widget. This property adds an ID for the widget: CounterButton( key: ValueKey('red'), color: Colors.red, ), By adding key, you've uniquely identified the red counter button and Flutter will be able to preserve its state. You can read more about using keys in the Medium article, Keys! What are they good for?.

Explain stateful widget's lifecycle in detail

createState initState() didChangeDependencies() build() didUpdateWidget() setState() deactivate() dispose() initState(): Flutter's initState() method is the first method that is used while creating a stateful class, here we can initialize variables, data, properties, etc. for any widget. createState(): When we create a stateful widget, our framework calls a createState() method and it must be overridden. build(): The build method is used each time the widget is rebuilt. This can happen either after calling initState, didChangeDependencies, didUpdateWidget, or when the state is changed via a call to setState didChangeDependencies(): This method is called immediately after initState and when dependency of the State object changes via InheritedWidget. didUpdateWidget(): This method is called whenever the widget configuration changes. A typical case is when a parent passes some variable to the children() widget via the constructor. deactivate(): It is used when the state is removed from the tree but before the current frame change can be re-inserted into another part of the tree dispose(): We use this method when we remove permanently like should release resource created by an object like stop animation

Demonstrate Dart isolate communication using ports by completing the following steps: 1. Give a function called downloadAndCompressTheInternet() to a new isolate. 2. Have it return the value 42.

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); } In this code, you: Create a port for receiving data from the new isolate. Create a new isolate, give it some work to do and provide it a means to send data back. Listen for any data message that the new isolate sends, then get rid of the isolate. Send the data back using the port that the main isolate is listening to. The internet decompression algorithm is still under development. :]


Ensembles d'études connexes

Rich Brown Marketing 2400 Test 2

View Set

CP110 Midterm #1 Short Answer Questions

View Set

Test 2 Chapter 14 Additional Definitions

View Set

Pure Substances: Elements and Compounds. Mixtures: Homogenous and Heterogenous.

View Set

Computer & Internet Literacy Exam 1

View Set

Nurs 311 Addictions Homework Assignment

View Set

Combo with Microbiology Chapter 15 and 8 others

View Set

Chapter 36: Cardiomyopathy & Valvular Heart Disease Evolve Practice Questions

View Set