{"id":3201,"date":"2021-11-22T01:35:36","date_gmt":"2021-11-22T09:35:36","guid":{"rendered":"https:\/\/wonghoi.humgar.com\/blog\/?p=3201"},"modified":"2021-11-26T16:38:03","modified_gmt":"2021-11-27T00:38:03","slug":"gui-paradigms-1-redux-flutter-react-translated-to-matlab","status":"publish","type":"post","link":"https:\/\/wonghoi.humgar.com\/blog\/2021\/11\/22\/gui-paradigms-1-redux-flutter-react-translated-to-matlab\/","title":{"rendered":"GUI Paradigms (1): Redux (Flutter\/React) translated to MATLAB"},"content":{"rendered":"\n<p>For GUI development, we often start with controls (or widgets) that user interact with and it&#8217;ll emit\/run the callback function we registered for the event when the event happens. <\/p>\n\n\n\n<p>Most of the time we just want to read the state\/properties of certain controls, process them, and update other controls to show the result. Model-View-Controller (MVC) puts strict boundaries between interaction, data processing and display.<\/p>\n\n\n\n<p>The most common schematic for MVC is a circle showing the cycle of Controller-&gt;Model-&gt;View, but in practice, it&#8217;s the controller that&#8217;s the brains. The view can simultaneously accept user interactions, such as a editable text box or a list. The model usually don&#8217;t directly update the view directly on its own like the idealized diagram.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/www.educative.io\/cdn-cgi\/image\/f=auto,fit=contain,w=600\/api\/page\/5029697680310272\/image\/download\/5354457941409792\" alt=\"MVC with User Action\"\/><figcaption>From https:\/\/www.educative.io\/blog\/mvc-tutorial<\/figcaption><\/figure><\/div>\n\n\n\n<p>With MVC, basically we are concentrating the control&#8217;s callbacks to the controller object instead of just letting each control&#8217;s callback interact with the data store (model) and view in an unstructured way.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>When learning Flutter, I was exposed to the Redux pattern (which came from React). Because the tutorials was designed around the language features of Dart, the documentation kind of obscured the essence of the idea (why do we want to do this) as it dwelt on the framework (structure can be refactored into a package). The docs talked a lot about boundaries but wasn&#8217;t clearly why they have to be meticulously observed, which I&#8217;ll get to later.<\/p>\n\n\n\n<p>The core inspiration in Redux\/BLoC is taking advantage of the concept of &#8216;listening to a data object for changes&#8217; (instead of UI controls\/widget events)!<\/p>\n\n\n\n<p>Instead of having the UI control&#8217;s callback directly change other UI control&#8217;s state (e.g. for display), we design a state vector\/dictionary\/struct\/class that holds contents (state variables) that we care. It doesn&#8217;t have to map 1-1 to input events or 1-1 to output display controls.<\/p>\n\n\n\n<p>When an user interaction (input) event emitted a callback, the control&#8217;s callback do whatever it needs to produce the value(s) for the relevant state variable(s) and change the state vector. The changed state vector will trigger the listener that scans for what needs to be updated to reflect the new state and change the states of the appropriate view UI controls.<\/p>\n\n\n\n<p>This way the input UI controls&#8217; callbacks do not have to micromanage what output UI controls to update, so it can focus on the business logic that generates the content pool that will be picked up by the view UI controls to display the results. In Redux, you are free to design your state variables to match more closely to the input data from UI controls or output\/view controls&#8217; state. I personally prefer a state vector design that is closer to the output view than input controls.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>The intuition above is not the complete\/exact Redux, especially with Dart\/Flutter\/React. We also have to to keep the state in ONE place and make the order of state changes (thus behavior) predictable!<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Actions and reducers are separate. Every input control fires a event (action signal) and we&#8217;ll wait until the reducers (registered to the actions) to pick it up during dispatch() instead of jumping on it. This way there&#8217;s only ONE place that can change states. Leave all the side effects in the control callback where you generate the action. No side effects (like changing other controls) allowed in reducers!<\/li><li>Reducers do not update the state in place (it&#8217;s read only). Always generate a new state vector to replace the old one (for performance, we&#8217;ll replace the state vector if we verified the contents actually changed). This will make timing predictable (you are stepping through state changes one by one)<\/li><\/ul>\n\n\n\n<p>In Javascript, there isn&#8217;t really a listener actively listening state variable changes. Dispatch (which will be called every time the user interacts using control) just runs through all the listeners registered at the very end after it has dispatched all the reducers. In MATLAB, you can optionally set the state vector to be Observable and attach the change listener callback instead of explicitly calling it within dispatch.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"834\" height=\"555\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image.png\" alt=\"\" class=\"wp-image-3203\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image.png 834w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-300x200.png 300w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-768x511.png 768w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-120x80.png 120w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-360x240.png 360w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-600x400.png 600w\" sizes=\"auto, (max-width: 834px) 100vw, 834px\" \/><figcaption>https:\/\/gist.github.com\/gaearon\/ffd88b0e4f00b22c3159<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Here is an example of a MATLAB class that captures the spirit of Redux. I added a 2 second delay to emulate long operations and used <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/15895-enable-disable-entire-figure-window\" target=\"_blank\" rel=\"noreferrer noopener\">enableDisableFig()<\/a> to avoid dealing with queuing user interactions while it&#8217;s going through a long operation.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">classdef ReduxStoreDemo &lt; handle\n\n    % Should be made private later\n    properties (SetAccess = private, SetObservable)\n        state % {count}\n    end\n        \n    methods (Static)\n        % Made static so reducer cannot call dispatch and indirectly do\n        % side effect or create loops\n        function state = reducer(state, action)        \n            % Can use str2fun(action) here or use a function map\n            switch action\n                case 'increment'\n                    fprintf('Wait 2 secs before incrementing\\n');\n                    pause(2)\n                    state.count = state.count + 1;\n                    fprintf('Incremented\\n');\n            end                \n        end        \n    end\n    \n    % We keep all the side-effect generating operations (such as\n    % temporarily changing states in the GUI) in dispatch() so\n    % there's only ONE PLACE where state can change\n    methods\n        function dispatch(obj, action, src, evt)\n            % Disable all figures during an interaction\n            figures = findobj(groot, 'type', 'figure');\n            old_fig_states = arrayfun(@(f) enableDisableFig(f, 'off'), figures);      \n            src.String = 'Wait ...';\n            \n                new_state = ReduxStoreDemo.reducer(obj.state, action);\n                \n                % Don't waste cycles updating nops \n                if( ~isequal(new_state, obj.state) )\n                    % MATLAB already have listeners attached.\n                    % So no need to scan listeners like React Redux            \n                    obj.state = new_state;\n                end                \n                \n            % Re-enable figure obj.controls after it's done\n            arrayfun(@(f, os) enableDisableFig(f, os), figures, old_fig_states);                        \n            src.String = 'Increment';\n        end\n    end\n    \n    methods\n        function obj = ReduxStoreDemo()\n            figure();            \n            obj.state.count = 0;                        \n            \n            h_1x  = uicontrol('style', 'text', 'String', '1x Box', ...\n                              'Units', 'Normalized', ...\n                              'Position', [0.1 0.3, 0.2, 0.1], ...\n                              'HorizontalAlignment', 'left');                          \n            addlistener(obj, 'state', ...\n                        'PostSet', @(varargin) obj.update_count_1x( h_1x , varargin{:})); \n                        \n            uicontrol('style', 'pushbutton', 'String', 'Increment', ...\n                      'Units', 'Normalized', ...\n                      'Position', [0.1 0.1, 0.15, 0.1], ...\n                      'Callback', @(varargin) obj.dispatch('increment', varargin{:}));                             \n            \n            % Force trigger the listeners to reflect the initial state\n            obj.state = obj.state;\n        end\n    end\n    \n    %% These are 'renders' registered when the uiobj.controls are created\n    % Should stick to reading off the state. Do not call dispatch here\n    % (just leave it for the next action to pick up the consequentials)\n    methods\n        % The (src, event) is useless for listeners because it's not the \n        % uicontrol handle but the state property's metainfo (access modifiers, etc)\n        function update_count_1x(obj, hObj, varargin)            \n            hObj.String = num2str(obj.state.count);\n        end        \n    end\n    \nend<\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"402\" height=\"188\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-3.png\" alt=\"\" class=\"wp-image-3211\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-3.png 402w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2021\/11\/image-3-300x140.png 300w\" sizes=\"auto, (max-width: 402px) 100vw, 402px\" \/><\/figure><\/div>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_3201\" class=\"pvc_stats all  \" data-element-id=\"3201\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>For GUI development, we often start with controls (or widgets) that user interact with and it&#8217;ll emit\/run the callback function we registered for the event when the event happens. Most of the time we just want to read the state\/properties &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2021\/11\/22\/gui-paradigms-1-redux-flutter-react-translated-to-matlab\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_3201\" class=\"pvc_stats all  \" data-element-id=\"3201\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[59,62,65,10,63],"tags":[],"class_list":["post-3201","post","type-post","status-publish","format-standard","hentry","category-concepts","category-dart","category-gui","category-matlab","category-programming-languages"],"_links":{"self":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/3201","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/comments?post=3201"}],"version-history":[{"count":6,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/3201\/revisions"}],"predecessor-version":[{"id":3233,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/3201\/revisions\/3233"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=3201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/categories?post=3201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/tags?post=3201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}