<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Open source packages used by Flutter.]]></title><description><![CDATA[I develop open source packages used in administrator services and CMS.
PlutoGrid, a data grid.
PlutoMenuBar to which you can add multiple submenus.]]></description><link>https://pluto.weblaze.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 23:42:05 GMT</lastBuildDate><atom:link href="https://pluto.weblaze.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The case of assigning the id of PlutoMenuItem.]]></title><description><![CDATA[A unique id can be set for PlutoMenuItem.
PlutoMenuItem(
  id: 'unique_id',
  title: 'Menu 1',
)

If PlutoMenuItem is created in build method, you should set a unique id for PlutoMenuItem.Otherwise, when PlutoMenuBar is rebuilt, incorrect actions suc...]]></description><link>https://pluto.weblaze.dev/the-case-of-assigning-the-id-of-plutomenuitem</link><guid isPermaLink="true">https://pluto.weblaze.dev/the-case-of-assigning-the-id-of-plutomenuitem</guid><category><![CDATA[Flutter]]></category><category><![CDATA[MenuBar]]></category><category><![CDATA[PlutoMenuBar]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Wed, 30 Nov 2022 03:55:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669780483544/wy_V0sg4F.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A unique <code>id</code> can be set for <code>PlutoMenuItem</code>.</p>
<pre><code class="lang-dart">PlutoMenuItem(
  id: <span class="hljs-string">'unique_id'</span>,
  title: <span class="hljs-string">'Menu 1'</span>,
)
</code></pre>
<p>If <code>PlutoMenuItem</code> is created in <code>build</code> method, you should set a unique <code>id</code> for <code>PlutoMenuItem</code>.<br />Otherwise, when <code>PlutoMenuBar</code> is rebuilt, incorrect actions such as flickering or closing of open menus may occur.<br />(<code>id</code> must be unique throughout the app.)</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> TestWidget({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> PlutoMenuBar(menus: [
      PlutoMenuItem(
        id: <span class="hljs-string">'Menu1'</span>,
        title: <span class="hljs-string">'Menu1'</span>,
      ),
      PlutoMenuItem(
        id: <span class="hljs-string">'Menu2'</span>,
        title: <span class="hljs-string">'Menu2'</span>,
      ),
    ]);
  }
}
</code></pre>
<p>If <code>PlutoMenuItem</code> is created only once in a method such as <code>initState</code>, there is no need to set <code>id</code>.<br />Even if <code>PlutoMenuBar</code> is rebuilt, <code>PlutoMenuItem</code> is reused.<br />(The internal <code>PlutoMenuItem.key</code> is not changed.)</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> TestWidget({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  State&lt;TestWidget&gt; createState() =&gt; _TestWidgetState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_TestWidgetState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">TestWidget</span>&gt; </span>{
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoMenuItem&gt; menus;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();

    menus = [
      PlutoMenuItem(title: <span class="hljs-string">'Menu1'</span>),
      PlutoMenuItem(title: <span class="hljs-string">'Menu2'</span>),
    ];
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> PlutoMenuBar(menus: menus);
  }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Scrollbar and Scroll Behavior]]></title><description><![CDATA[The default ScrollBehavior.dragDevices of PlutoGrid is as follows.(dragDevices is a setting that allows scrolling by dragging to the set pointer type.)
// In case of Mobile
{
  PointerDeviceKind.touch,
  PointerDeviceKind.stylus,
  PointerDeviceKind....]]></description><link>https://pluto.weblaze.dev/scrollbar-and-scroll-behavior</link><guid isPermaLink="true">https://pluto.weblaze.dev/scrollbar-and-scroll-behavior</guid><category><![CDATA[scrollbar]]></category><category><![CDATA[hovered-scrollbar]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[scroll]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Tue, 29 Nov 2022 00:33:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669681202936/SNkTPqONP.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The default <code>ScrollBehavior.dragDevices</code> of <code>PlutoGrid</code> is as follows.<br />(dragDevices is a setting that allows scrolling by dragging to the set pointer type.)</p>
<pre><code><span class="hljs-comment">// In case of Mobile</span>
{
  PointerDeviceKind.touch,
  PointerDeviceKind.stylus,
  PointerDeviceKind.invertedStylus,
  PointerDeviceKind.unknown,
}

<span class="hljs-comment">// In case of desktop</span>
{
  PointerDeviceKind.mouse,
  PointerDeviceKind.trackpad,
  PointerDeviceKind.unknown,
}
</code></pre><p>Since <code>PointerDeviceKind.mouse</code> is set in Desktop, it is difficult to select the text of <code>PlutoCell</code> by dragging (because it is scrolled by dragging).<br />In this case, you can customize <code>dragDevices</code> to remove <code>PointerDeviceKind.mouse</code>.</p>
<pre><code class="lang-dart">PlutoGrid(
  configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(
    scrollbar: PlutoGridScrollbarConfig(
      dragDevices: {
        <span class="hljs-comment">// PointerDeviceKind.mouse,</span>
        PointerDeviceKind.trackpad,
        PointerDeviceKind.unknown,
      },
    ),
  ),
),
</code></pre>
<p>If you remove <code>PointerDeviceKind.mouse</code> from Desktop to remove the drag-to-scroll action, horizontal or vertical scrolling can be done with the scroll bar.<br />In the case of Desktop, the scroll bar appears when hovering the mouse over the scroll bar area.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669680861925/c4a9Vnr2v.gif" alt="pluto_grid_scrollbar.gif" /></p>
]]></content:encoded></item><item><title><![CDATA[PlutoMenuItemWidget]]></title><description><![CDATA[Example code to add custom widget to PlutoMenuBar.
One thing to note is that if a custom widget has a state change, such as the Slider in the example below, the state change must be handled by the custom widget.
In the example below, if you call setS...]]></description><link>https://pluto.weblaze.dev/plutomenuitemwidget</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutomenuitemwidget</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoMenuBar]]></category><category><![CDATA[MenuBar]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Thu, 24 Nov 2022 21:09:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669324125483/O2uURDSuw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Example code to add custom widget to <code>PlutoMenuBar</code>.</p>
<p>One thing to note is that if a custom widget has a state change, such as the <code>Slider</code> in the example below, the state change must be handled by the custom widget.
In the example below, if you call <code>setState</code> on the <code>_MyHomePageState</code> widget to change the <code>Slider's</code> value, the entire <code>PlutoMenuBar</code> widget will be rebuilt, which can cause the menu to close as soon as you change the <code>Slider</code>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ignore_for_file: avoid_print</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:pluto_menu_bar/pluto_menu_bar.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(<span class="hljs-keyword">const</span> MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'PlutoMenuBar Demo'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: <span class="hljs-keyword">const</span> MyHomePage(),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyHomePage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  State&lt;MyHomePage&gt; createState() =&gt; _MyHomePageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyHomePage</span>&gt; </span>{
  <span class="hljs-keyword">final</span> TextEditingController _textEditingController = TextEditingController();

  <span class="hljs-keyword">final</span> _SliderValue _sliderValue = _SliderValue();

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();

    _textEditingController.addListener(_onChangedText);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _textEditingController.removeListener(_onChangedText);

    _textEditingController.dispose();

    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-keyword">void</span> _onChangedText() {
    <span class="hljs-built_in">print</span>(_textEditingController.text);
  }

  <span class="hljs-keyword">void</span> _onChangedSlider(<span class="hljs-built_in">double</span> value) {
    <span class="hljs-built_in">print</span>(value);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: PlutoMenuBar(
        mode: PlutoMenuBarMode.hover,
        menus: [
          PlutoMenuItem(
            title: <span class="hljs-string">'CustomWidgets'</span>,
            children: [
              PlutoMenuItem.widget(
                widget: _TextField(controller: _textEditingController),
              ),
              PlutoMenuItem.widget(
                widget: _Slider(
                  sliderValue: _sliderValue,
                  onChanged: _onChangedSlider,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_TextField</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> _TextField({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.controller});

  <span class="hljs-keyword">final</span> TextEditingController controller;

  <span class="hljs-meta">@override</span>
  State&lt;_TextField&gt; createState() =&gt; _TextFieldState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_TextFieldState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">_TextField</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> TextField(controller: widget.controller);
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_SliderValue</span> </span>{
  <span class="hljs-built_in">double</span> value = <span class="hljs-number">0</span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_Slider</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> _Slider({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.sliderValue,
    <span class="hljs-keyword">this</span>.onChanged,
  });

  <span class="hljs-keyword">final</span> _SliderValue sliderValue;

  <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-built_in">Function</span>(<span class="hljs-built_in">double</span>)? onChanged;

  <span class="hljs-meta">@override</span>
  State&lt;_Slider&gt; createState() =&gt; _SliderState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_SliderState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">_Slider</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Slider(
      value: widget.sliderValue.value,
      divisions: <span class="hljs-number">5</span>,
      max: <span class="hljs-number">100</span>,
      onChanged: (value) {
        setState(() {
          widget.sliderValue.value = value;
          <span class="hljs-keyword">if</span> (widget.onChanged != <span class="hljs-keyword">null</span>) {
            widget.onChanged!(value);
          }
        });
      },
    );
  }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Shortcuts]]></title><description><![CDATA[PlutoGrid has default shortcut keys set, and users can change or set shortcut keys.
Shortcut Range
The scope of the shortcut depends on the focus.

PlutoGrid : In the state that TextField is not focused.
 You can set custom shortcut keys.
TextField :...]]></description><link>https://pluto.weblaze.dev/shortcuts</link><guid isPermaLink="true">https://pluto.weblaze.dev/shortcuts</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[Shortcuts]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Fri, 04 Nov 2022 19:57:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667591687853/AOOHskS6-.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>PlutoGrid</code> has default shortcut keys set, and users can change or set shortcut keys.</p>
<h4 id="heading-shortcut-range">Shortcut Range</h4>
<p>The scope of the shortcut depends on the focus.</p>
<ul>
<li><code>PlutoGrid</code> : In the state that <code>TextField</code> is not focused.
 You can set custom shortcut keys.</li>
<li><code>TextField</code> : In case the cell in edit state or <code>TextField</code> of column filtering is focused.
 You cannot set custom shortcut keys.</li>
</ul>
<h4 id="heading-customizable-shortcuts-in-the-plutogrid-range">Customizable shortcuts in the <code>PlutoGrid</code> range</h4>
<ul>
<li>ArrowKeys: Moves the focus of the current cell up, down, left and right.</li>
<li>Shift + ArrowKeys : Moves a cell or row up, down, left and right to select.</li>
<li>PageUp/Down : Moves the focus of the current cell up and down by page unit.</li>
<li>Shift + PageUp/Down : Moves cells or rows up and down and selects in units of pages.</li>
<li>Alt + PageUp/Down : Moves the current page when pagination is enabled.</li>
<li>Tab or Shift + Tab : Moves the focus of the current cell to the left or right.
If you hit the left and right ends, set <code>PlutoGridConfiguration.tabKeyAction</code> to <code>moveToNextOnEdge</code> to continue moving.</li>
<li>Enter or Shift + Enter : Sets the current cell to edit state or calls the <code>PlutoGrid.onSelected</code> callback if <code>PlutoGird</code> is in selection mode.
Additional actions can be set with <code>PlutoGridConfiguration.enterKeyAction</code>.</li>
<li>Escape: When <code>PlutoGird</code> is in selection mode, <code>PlutoGrid.onSelected</code> is called with <code>PlutoGridOnSelectedEvent</code> containing a null value which means cancel the selection.
In the non-selection mode, the edit state of the cell is canceled.</li>
<li>Home or End: Moves the focus of the current cell to the left and right ends.</li>
<li>Control + Home/End : Moves the focus of the current cell up and down.</li>
<li>Shift + Home/End : Select cells all the way to the left or right.</li>
<li>Control + Shift + Home/End : Select a cell or row all the way up, down, left, and right.</li>
<li>F2 : Set the cell to the edit state. In case of date, time, or selection type column, the popup is called.</li>
<li>F3 : If the current cell is in the focused state, the focus moves to the column filter of the current cell.
This function is valid only when column filter is enabled with <code>PlutoGridStateManager.setShowColumnFilter</code>.
If the <code>F3</code> key is pressed once again while the focus is moved to the column filter, the column filter pop-up is called.
Also, if you press the <code>Escape</code> key in the column filter focus, the focus is moved to the cell before moving.</li>
<li>F4 : Toggles column sorting. Only valid if <code>PlutoColumn.enableSorting</code> is enabled.</li>
<li>Control + C : Copies the values ​​of the current cell or row or the selected cell or row to the clipboard.</li>
<li>Control + V : Paste values ​​into the current cell or row or the selected cell or row.</li>
<li>Control + A : Select all cells or rows.</li>
</ul>
<h4 id="heading-how-to-set-custom-shortcut-keys">How to set custom shortcut keys</h4>
<pre><code class="lang-dart">PlutoGrid(
  configuration: PlutoGridConfiguration(
    shortcut: PlutoGridShortcut(
      actions: {
        <span class="hljs-comment">// This is a Map with basic shortcut keys and actions set.</span>
        ...PlutoGridShortcut.defaultActions,
        <span class="hljs-comment">// You can override the enter key behavior as below.</span>
        LogicalKeySet(LogicalKeyboardKey.enter): CustomEnterKeyAction(),
      },
    ),
  ),
);

<span class="hljs-comment">// Create a new class that inherits from PlutoGridShortcutAction</span>
<span class="hljs-comment">// If the execute method is implemented, </span>
<span class="hljs-comment">// the implemented method is executed when the enter key is pressed.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomEnterKeyAction</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PlutoGridShortcutAction</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> execute({
    <span class="hljs-keyword">required</span> PlutoKeyManagerEvent keyEvent,
    <span class="hljs-keyword">required</span> PlutoGridStateManager stateManager,
  }) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Pressed enter key.'</span>);
  }
}
</code></pre>
<blockquote>
<p>If you have any questions or need to add features, please register on GitHub.
  <a target="_blank" href="https://github.com/bosskmk/pluto_grid">PlutoGrid Github</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Introduction to PlutoLayout]]></title><description><![CDATA[PlutoLayout is a Flutter package for configuring layouts such as CMS or admin service.You can configure tab views or menus on the top, bottom, left, and right sides.It also supports keyboard shortcuts and allows you to toggle or resize the tab view.
...]]></description><link>https://pluto.weblaze.dev/introduction-to-plutolayout</link><guid isPermaLink="true">https://pluto.weblaze.dev/introduction-to-plutolayout</guid><category><![CDATA[PlutoLayout]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[layout]]></category><category><![CDATA[cms]]></category><category><![CDATA[admin]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Fri, 04 Nov 2022 16:34:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667579582973/Av6MbZykO.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>PlutoLayout</code> is a <code>Flutter</code> package for configuring layouts such as CMS or admin service.<br />You can configure tab views or menus on the top, bottom, left, and right sides.<br />It also supports keyboard shortcuts and allows you to toggle or resize the tab view.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668461581927/PuOGdkofk.gif" alt="pluto_layout_0.3.gif" /></p>
<h4 id="heading-default-container-plutolayoutcontainer">Default container <code>PlutoLayoutContainer</code></h4>
<p>You need to set the body container by default, and you can optionally add containers to the <code>top</code>, <code>bottom</code>, <code>left</code>, and <code>right</code> sides.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> layout = PlutoLayout(
  body: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'body container'</span>),
  ),    
  top: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'top container'</span>),
  ),
  bottom: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'bottom container'</span>),
  ),
  left: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'left container'</span>),
  ),
  right: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'right container'</span>),
  ),
);
</code></pre>
<h4 id="heading-tab-widgets-plutolayouttabs">Tab Widgets <code>PlutoLayoutTabs</code></h4>
<p>The tab widget consists of a menu button and a tab view area that is opened or closed when the menu button is toggled.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> layout = PlutoLayout(
  <span class="hljs-comment">// This is a property that should be set by default. </span>
  <span class="hljs-comment">// It is centered in the layout.</span>
  body: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'body container'</span>),
  ),
  <span class="hljs-comment">// This is an attribute that can be optionally added.</span>
  left: PlutoLayoutContainer(
    child: PlutoLayoutTabs(
      <span class="hljs-comment">// This property sets the size of the tab view.</span>
      <span class="hljs-comment">// If the size of the tab view is left or right, it means the width.</span>
      <span class="hljs-comment">// In the case of top and bottom, it means the height.</span>
      <span class="hljs-comment">// In this example code, the left property is set, </span>
      <span class="hljs-comment">// so the size means the width.</span>
      tabViewSizeResolver: PlutoLayoutTabViewSizeConstrains(
        minSize: <span class="hljs-number">100</span>,
        maxSize: <span class="hljs-number">500</span>,
        initialSize: <span class="hljs-number">300</span>,
      ),
      items: [
        PlutoLayoutTabItem(
          id: <span class="hljs-string">'Files'</span>,
          title: <span class="hljs-string">'Files'</span>,
          icon: Icon(Icons.file_present),
          <span class="hljs-comment">// This property sets the size of each tab view.</span>
          <span class="hljs-comment">// If the size of each tab view is left or right, it means the height.</span>
          <span class="hljs-comment">// In the case of top and bottom, it means the width.</span>
          <span class="hljs-comment">// In this example code, the left property is set, </span>
          <span class="hljs-comment">// so size means height.</span>
          sizeResolver: PlutoLayoutTabItemSizeFlexible(<span class="hljs-number">0.3</span>),
          <span class="hljs-comment">// This is a callback function that returns a widget </span>
          <span class="hljs-comment">// that is displayed when the tab menu button is tapped.</span>
          tabViewWidget: Text(<span class="hljs-string">'Files tab view'</span>),
        ),
        PlutoLayoutTabItem(
          id: <span class="hljs-string">'Users'</span>,
          title: <span class="hljs-string">'Users'</span>,
          icon: Icon(Icons.account_box),
          sizeResolver: PlutoLayoutTabItemSizeFlexible(<span class="hljs-number">0.7</span>),
          tabViewWidget: Text(<span class="hljs-string">'Users tab view'</span>),
        ),
      ],
    ),
  ),
);
</code></pre>
<h4 id="heading-additional-explanation-of-tab-widget-size-setting">Additional explanation of tab widget size setting</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667578232136/EXpffJxbu.png" alt="tabview.png" /></p>
<p>The size of the tab view that appears when the menu of the tab widget is toggled depends on the direction in which the tab widget is positioned.<br />In the case of left and right tabs, the size of the tab view means the width.<br />For top and bottom tabs, the size of the tab view means the height.<br />Here, a tab view means a container that wraps all tab items.</p>
<p>Also, in the case of the size of the tab view of each item,<br />In the case of left and right tabs, the size of the tab view of each item means the height.<br />In the case of top and bottom tabs, the size of the tab view of each item means the width.</p>
<h5 id="heading-size-of-tabviewsize">Size of <code>TabViewSize</code></h5>
<ul>
<li>The size of <code>TabViewSize</code> is set by adding <code>tabViewSizeResolver</code> property of <code>PlutoLayoutTabs</code>.<br />If not set, the size is automatically set to fit the screen size.</li>
<li>Set <code>PlutoLayoutTabViewSizeFixed</code> and <code>PlutoLayoutTabViewSizeRatio</code> to a fixed size.<br />Once set, the size cannot be changed, and when the size of the parent widget of <code>PlutoLayout</code> is changed, the size may change automatically.</li>
<li><code>PlutoLayoutTabViewSizeConstrains</code> can limit the minimum and maximum sizes, and you can also set the default size.<br />You can change the size after it is set.</li>
</ul>
<h5 id="heading-size-of-tabviewitemsize">Size of <code>TabViewItemSize</code></h5>
<ul>
<li>The size of <code>TabViewItemSize</code> is set by adding the <code>sizeResolver</code> property of each <code>PlutoLayoutTabItem</code>.
If not set, <code>PlutoLayoutTabItemSizeFlexible</code> is set to a value of <code>1</code>.</li>
<li><code>PlutoLayoutTabItemSizeFlexible</code> can pass a value between <code>0</code> and <code>1</code>, and will be set to <code>1</code> if not passed.
When two items are activated, assuming that both items have a value of <code>1</code>, the size is set in a ratio of <code>1:1</code>.</li>
<li><code>PlutoLayoutTabItemSizeInitial</code> sets the initial size when the tab item is activated.</li>
</ul>
<h4 id="heading-shortcut-settings">Shortcut settings</h4>
<p>Shortcuts work with Flutter's <code>Shortcuts</code> widget.
You can pass desired shortcuts and Actions to the <code>shortcuts</code> property of <code>PlutoLayout</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> layout = PlutoLayout(
  shortcuts: {
    <span class="hljs-comment">// Pressing the Escape key closes all currently open tab views.</span>
    LogicalKeySet(LogicalKeyboardKey.escape):
        PlutoLayoutActions.hideAllTabView(),
    <span class="hljs-comment">// When Alt + Number 1 is pressed, </span>
    <span class="hljs-comment">// the items registered in the left tab are opened and closed in order.</span>
    LogicalKeySet(LogicalKeyboardKey.alt, LogicalKeyboardKey.digit1):
        PlutoLayoutActions.rotateTabView(
      PlutoLayoutContainerDirection.left,
    ),
    <span class="hljs-comment">// If you press Alt + Number 2, </span>
    <span class="hljs-comment">// the items registered in the right tab are opened and closed in order.</span>
    LogicalKeySet(LogicalKeyboardKey.alt, LogicalKeyboardKey.digit2):
        PlutoLayoutActions.rotateTabView(
      PlutoLayoutContainerDirection.right,
    ),
    <span class="hljs-comment">// Press Alt + Right Arrow to increase the size </span>
    <span class="hljs-comment">// of the currently focused tab view.</span>
    <span class="hljs-comment">// The reverseByDirection property is a property to change </span>
    <span class="hljs-comment">// the width according to the logic according to the left or right tab view.</span>
    <span class="hljs-comment">// For example, in the case of the left tab view, </span>
    <span class="hljs-comment">// we want to increase it by pressing the right arrow key.</span>
    <span class="hljs-comment">// But in the case of the right tab view, </span>
    <span class="hljs-comment">// we want it to be decreased when the right arrow key is pressed.</span>
    <span class="hljs-comment">// Each shortcut can be assigned, </span>
    <span class="hljs-comment">// but if you need to increase/decrease according to the logic with one shortcut</span>
    <span class="hljs-comment">// Just set the reverseByDirection property to true.</span>
    LogicalKeySet(LogicalKeyboardKey.alt, LogicalKeyboardKey.arrowRight):
        PlutoLayoutActions.increaseTabView(reverseByDirection: <span class="hljs-keyword">true</span>),
    <span class="hljs-comment">// Press Alt + Left arrow key to decrease the size of the currently focused tab view.</span>
    LogicalKeySet(LogicalKeyboardKey.alt, LogicalKeyboardKey.arrowLeft):
        PlutoLayoutActions.decreaseTabView(reverseByDirection: <span class="hljs-keyword">true</span>),
  },
  body: PlutoLayoutContainer(
    child: Text(<span class="hljs-string">'body container'</span>),
  ),
);
</code></pre>
<blockquote>
<p>If you have additional questions or features that need to be added, please register in the GitHub issue.<br />  <a target="_blank" href="https://github.com/bosskmk/pluto_layout">PlutoLayout Github</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[PlutoGridMode]]></title><description><![CDATA[The modes of PlutoGrid are normal, readOnly, select, selectWithOneTap, multiSelect, and popup.The popup mode is intended to be used by internal widgets.The rest of the modes are used as follows according to the user's purpose.
normal mode
As a basic ...]]></description><link>https://pluto.weblaze.dev/plutogridmode</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutogridmode</guid><category><![CDATA[Flutter]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[PlutoGrid]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Sun, 30 Oct 2022 13:41:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667137107616/4jdQX-lc9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The modes of PlutoGrid are <code>normal</code>, <code>readOnly</code>, <code>select</code>, <code>selectWithOneTap</code>, <code>multiSelect</code>, and <code>popup</code>.<br />The <code>popup</code> mode is intended to be used by internal widgets.<br />The rest of the modes are used as follows according to the user's purpose.</p>
<h4 id="heading-normal-mode"><code>normal</code> mode</h4>
<p>As a basic mode, there are no restrictions on editing and operation.</p>
<h4 id="heading-readonly-mode"><code>readOnly</code> mode</h4>
<p>In this mode, cell editing is restricted.</p>
<h4 id="heading-select-or-selectwithonetap-mode"><code>select</code> or <code>selectWithOneTap</code> mode</h4>
<p>This mode is for single row selection. When a row is selected, the <code>PlutoGrid.onSelected</code> callback is called.<br />In <code>select</code> mode, the <code>PlutoGrid.onSelected</code> callback is called only when the currently selected row is tapped or the enter key is pressed.<br />The <code>selectWithOneTap</code> mode will call the <code>PlutoGrid.onSelected</code> callback when a row is tapped, regardless of whether a row is currently selected or not.<br />The selected row is passed as the <code>row</code>, <code>rowIdx,</code>cell<code>values ​​of the</code>onSelected` callback.</p>
<h4 id="heading-multiselect-mode"><code>multiSelect</code> mode</h4>
<p>This mode is for selecting multiple rows. You can select or deselect rows with a single tap.<br />You can also select rows with the keyboard <code>shift + arrowUp/Down</code>, <code>shift + tap</code>, <code>control + tap</code>, etc.<br />However, when selecting a row with the keyboard, the <code>PlutoGrid.onSelected</code> callback is called only when the <code>enter</code> key is pressed.<br />Selected rows are listed in the <code>List&lt;PlutoRow&gt;? passed as the selectedRows</code> value.<br />Typing <code>escape</code> will undo all selected rows. In this case, <code>selectedRows</code> returns null.</p>
<h4 id="heading-the-mode-can-be-changed-to-setstate-of-statefulwidget-as-shown-below">The mode can be changed to <code>setState</code> of <code>StatefulWidget</code> as shown below.</h4>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:pluto_grid/pluto_grid.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'../dummy_data/development.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmptyScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> routeName = <span class="hljs-string">'empty'</span>;

  <span class="hljs-keyword">const</span> EmptyScreen({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  _EmptyScreenState createState() =&gt; _EmptyScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_EmptyScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">EmptyScreen</span>&gt; </span>{
  <span class="hljs-keyword">late</span> <span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns;

  <span class="hljs-keyword">late</span> <span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows;

  <span class="hljs-keyword">late</span> PlutoGridStateManager stateManager;

  PlutoGridMode mode = PlutoGridMode.normal;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();

    <span class="hljs-keyword">final</span> dummyData = DummyData(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>);

    columns = dummyData.columns;

    rows = dummyData.rows;
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: SafeArea(
        child: Container(
          padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
          child: PlutoGrid(
            mode: mode,
            columns: columns,
            rows: rows,
            onChanged: (PlutoGridOnChangedEvent event) {
              <span class="hljs-built_in">print</span>(event);
            },
            onLoaded: (PlutoGridOnLoadedEvent event) {
              stateManager = event.stateManager;
            },
            createHeader: (s) =&gt; TextButton(
              onPressed: () {
                setState(() {
                  mode = mode.isNormal == <span class="hljs-keyword">true</span>
                      ? PlutoGridMode.readOnly
                      : PlutoGridMode.normal;
                });
              },
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Change mode'</span>),
            ),
            configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(),
          ),
        ),
      ),
    );
  }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Rows from server-side.]]></title><description><![CDATA[This is a function to call rows from the server side added in PlutoGrid 5.3 version.
There are two ways to load and process data on the server side.
The pagination method used in the DB structure where the total number of pages is known and the infin...]]></description><link>https://pluto.weblaze.dev/rows-from-server-side</link><guid isPermaLink="true">https://pluto.weblaze.dev/rows-from-server-side</guid><category><![CDATA[server-side pagination]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[infinite scrolling]]></category><category><![CDATA[datagrid]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Sat, 22 Oct 2022 06:02:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1666418472524/NBA6ncRlw.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a function to call rows from the server side added in <code>PlutoGrid 5.3</code> version.
There are two ways to load and process data on the server side.</p>
<p>The pagination method used in the DB structure where the total number of pages is known and the infinite scroll method used in the DB structure where the total number of pages is unknown.</p>
<h3 id="heading-demo-links">Demo links</h3>
<p>PlutoLazyPagination<br />https://weblaze.dev/pluto_grid/build/web/#feature/row-lazy-pagination<br />PlutoInfinityScrollRows<br />https://weblaze.dev/pluto_grid/build/web/#feature/row-infinity-scroll</p>
<h3 id="heading-plutolazypagination">PlutoLazyPagination</h3>
<p>This is a pagination widget.<br />Return a <code>PlutoLazyPagination</code> widget to the <code>createFooter</code> callback of <code>PlutoGrid</code> as shown below.<br />Pagination widget is created at the bottom of <code>PlutoGrid</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> columns = [
  PlutoColumn(
    title: <span class="hljs-string">'Column'</span>, 
    field: <span class="hljs-string">'column'</span>, 
    type: PlutoColumnType.text()
  ),
];

<span class="hljs-comment">// Pass an empty list to PlutoGrid.</span>
<span class="hljs-comment">// After PlutoGrid is loaded, page 1 is automatically called.</span>
<span class="hljs-comment">// initialPage defaults to 1, initialFetch defaults to true, if you change this</span>
<span class="hljs-comment">// You can change the page to be loaded first or set not to load the page first.</span>
<span class="hljs-keyword">final</span> rows = [];

PlutoGrid(
  columns: columns,
  rows: rows,
  createFooter: (stateManager) {
    <span class="hljs-keyword">return</span> PlutoLazyPagination(
      <span class="hljs-comment">// Determine the first page.</span>
      <span class="hljs-comment">// Default is 1.</span>
      initialPage: <span class="hljs-number">1</span>,

      <span class="hljs-comment">// First call the fetch function to determine whether to load the page.</span>
      <span class="hljs-comment">// Default is true.</span>
      initialFetch: <span class="hljs-keyword">true</span>,

      <span class="hljs-comment">// Decide whether sorting will be handled by the server.</span>
      <span class="hljs-comment">// If false, handle sorting on the client side.</span>
      <span class="hljs-comment">// Default is true.</span>
      fetchWithSorting: <span class="hljs-keyword">true</span>,

      <span class="hljs-comment">// Decide whether filtering is handled by the server.</span>
      <span class="hljs-comment">// If false, handle filtering on the client side.</span>
      <span class="hljs-comment">// Default is true.</span>
      fetchWithFiltering: <span class="hljs-keyword">true</span>,

      <span class="hljs-comment">// Determines the page size to move to the previous and next page buttons.</span>
      <span class="hljs-comment">// Default value is null. In this case,</span>
      <span class="hljs-comment">// it moves as many as the number of page buttons visible on the screen.</span>
      pageSizeToMove: <span class="hljs-keyword">null</span>,

      <span class="hljs-comment">// Implement a callback function to get data from the server. explained below.</span>
      fetch: fetch, 
      stateManager: stateManager,
    );
  },
)
</code></pre>
<p>Implementation description of <code>fetch</code> function of <code>PlutoLazyPagination</code></p>
<pre><code class="lang-dart"><span class="hljs-comment">// Referring to the request data of the PlutoLazyPaginationRequest type,</span>
<span class="hljs-comment">// Implement a function that returns a PlutoLazyPaginationResponse response value.</span>
Future&lt;PlutoLazyPaginationResponse&gt; fetch(
  PlutoLazyPaginationRequest request,
) <span class="hljs-keyword">async</span> {
  <span class="hljs-built_in">String</span> queryString = <span class="hljs-string">'?page=<span class="hljs-subst">${request.page}</span>'</span>;

  <span class="hljs-comment">// If filtering is applied, there is at least one element in request.filterRows .</span>
  <span class="hljs-keyword">if</span> (request.filterRows.isNotEmpty) {
    <span class="hljs-comment">// request.filterRows is a List&lt;PlutoRow&gt; type and contains filtering information.</span>
    <span class="hljs-keyword">final</span> filterMap = FilterHelper.convertRowsToMap(request.filterRows);
    <span class="hljs-comment">// Assume that the column has two filters, 123 and a, as a Contains type filter.</span>
    <span class="hljs-comment">// {column: [{Contains: 123}, {Contains: a}]}</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> filter <span class="hljs-keyword">in</span> filterMap.entries) {
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> type <span class="hljs-keyword">in</span> filter.value) {
        queryString += <span class="hljs-string">'&amp;filter[<span class="hljs-subst">${filter.key}</span>]'</span>;
        <span class="hljs-keyword">final</span> filterType = type.entries.first;
        queryString += <span class="hljs-string">'[<span class="hljs-subst">${filterType.key}</span>][]=<span class="hljs-subst">${filterType.value}</span>'</span>;
      }
    }
  }

  <span class="hljs-comment">// If a column in a sorted state exists.</span>
  <span class="hljs-keyword">if</span> (request.sortColumn != <span class="hljs-keyword">null</span> &amp;&amp; !request.sortColumn!.sort.isNone) {
    queryString += <span class="hljs-string">'&amp;sort=<span class="hljs-subst">${request.sortColumn!.field}</span>,<span class="hljs-subst">${request.sortColumn!.sort.name}</span>'</span>;
  }

  <span class="hljs-comment">// Make API requests with queryString.</span>
  <span class="hljs-built_in">print</span>(queryString);
  <span class="hljs-comment">// ?page=10&amp;filter[column][Contains][]=123&amp;filter[column][Contains][]=a&amp;sort=column,ascending</span>
  <span class="hljs-keyword">final</span> dataFromServer = <span class="hljs-keyword">await</span> Future.value(<span class="hljs-string">"""
    {
      "totalPage": 10,
      "data": [
        {
          "column": "value 1"
        },
        {
          "column": "value 2"
        }
      ]
    }
    """</span>);

  <span class="hljs-comment">// Converts the json format received from the server to the Map format.</span>
  <span class="hljs-keyword">final</span> parsedData = jsonDecode(dataFromServer);

  <span class="hljs-comment">// Create a PlutoRow.</span>
  <span class="hljs-keyword">final</span> rows = parsedData.data.map&lt;PlutoRow&gt;((rowData) {
    <span class="hljs-keyword">return</span> PlutoRow.fromJson(rowData);
  });

  <span class="hljs-keyword">return</span> PlutoLazyPaginationResponse(
    totalPage: parsedData[<span class="hljs-string">'totalPage'</span>],
    rows: rows.toList(),
  );
}
</code></pre>
<h3 id="heading-plutoinfinityscrollrows">PlutoInfinityScrollRows</h3>
<p>It is an infinite scrolling widget.<br />Return the <code>PlutoInfinityScrollRows</code> widget to the <code>createFooter</code> callback of <code>PlutoGrid</code> as shown below.<br />No widgets are created at the bottom of the <code>PlutoGrid</code>. (Created by SizedBox.shrink.)<br />No widgets need to be added, but if you need it for a special case, please ask.<br />I will update the function to add widgets.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> columns = [
  PlutoColumn(
    title: <span class="hljs-string">'Id'</span>, 
    field: <span class="hljs-string">'id'</span>, 
    type: PlutoColumnType.text()
  ),
  PlutoColumn(
    title: <span class="hljs-string">'Name'</span>, 
    field: <span class="hljs-string">'name'</span>, 
    type: PlutoColumnType.text()
  ),
];

<span class="hljs-comment">// Pass an empty list to PlutoGrid.</span>
<span class="hljs-comment">// Call the fetch function automatically after PlutoGrid is loaded.</span>
<span class="hljs-comment">// initialFetch default value is true, if you change this</span>
<span class="hljs-comment">// You can set it not to load the page at first.</span>
<span class="hljs-keyword">final</span> rows = [];

PlutoGrid(
  columns: columns,
  rows: rows,
  createFooter: (stateManager) =&gt; PlutoInfinityScrollRows(
    <span class="hljs-comment">// First call the fetch function to determine whether to load the page.</span>
    <span class="hljs-comment">// Default is true.</span>
    initialFetch: <span class="hljs-keyword">true</span>,

    <span class="hljs-comment">// Decide whether sorting will be handled by the server.</span>
    <span class="hljs-comment">// If false, handle sorting on the client side.</span>
    <span class="hljs-comment">// Default is true.</span>
    fetchWithSorting: <span class="hljs-keyword">true</span>,

    <span class="hljs-comment">// Decide whether filtering is handled by the server.</span>
    <span class="hljs-comment">// If false, handle filtering on the client side.</span>
    <span class="hljs-comment">// Default is true.</span>
    fetchWithFiltering: <span class="hljs-keyword">true</span>,

    <span class="hljs-comment">// Implement a callback function to get data from the server. explained below.</span>
    fetch: fetch,
    stateManager: stateManager,
  ),   
)
</code></pre>
<p>Implementation description of <code>fetch</code> function of <code>PlutoInfinityScrollRows</code></p>
<pre><code class="lang-dart">Future&lt;PlutoInfinityScrollRowsResponse&gt; fetch(
 PlutoInfinityScrollRowsRequest request,
) <span class="hljs-keyword">async</span> {
  <span class="hljs-built_in">String</span> queryString = <span class="hljs-string">'?'</span>;

  <span class="hljs-comment">// If lastRow is null, the first page can be loaded from the server.</span>
  <span class="hljs-comment">// If lastRow is present, add the user's unique value to the querystring.</span>
  <span class="hljs-keyword">if</span> (request.lastRow == <span class="hljs-keyword">null</span>) {
    queryString += <span class="hljs-string">'lastId='</span>;
  } <span class="hljs-keyword">else</span> {
    queryString += <span class="hljs-string">'lastId=<span class="hljs-subst">${request.lastRow!.cells[<span class="hljs-string">'id'</span>]}</span>'</span>;
  }

  <span class="hljs-comment">// At least one element is present in request.filterRows if filtering is applied.</span>
  <span class="hljs-keyword">if</span> (request.filterRows.isNotEmpty) {
    <span class="hljs-comment">// request.filterRows is a List&lt;PlutoRow&gt; type and contains filtering information.</span>
    <span class="hljs-keyword">final</span> filterMap = FilterHelper.convertRowsToMap(request.filterRows);
    <span class="hljs-comment">// Assume that two filters, mike and jessi, are set as a Contains type filter in name.</span>
    <span class="hljs-comment">// {name: [{Contains: mike}, {Contains: jessi}]}</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> filter <span class="hljs-keyword">in</span> filterMap.entries) {
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> type <span class="hljs-keyword">in</span> filter.value) {
        queryString += <span class="hljs-string">'&amp;filter[<span class="hljs-subst">${filter.key}</span>]'</span>;
        <span class="hljs-keyword">final</span> filterType = type.entries.first;
        queryString += <span class="hljs-string">'[<span class="hljs-subst">${filterType.key}</span>][]=<span class="hljs-subst">${filterType.value}</span>'</span>;
      }
    }
  }

  <span class="hljs-comment">// If a column in a sorted state exists.</span>
  <span class="hljs-keyword">if</span> (request.sortColumn != <span class="hljs-keyword">null</span> &amp;&amp; !request.sortColumn!.sort.isNone) {
    queryString += <span class="hljs-string">'&amp;sort=<span class="hljs-subst">${request.sortColumn!.field}</span>,<span class="hljs-subst">${request.sortColumn!.sort.name}</span>'</span>;
  }

  <span class="hljs-comment">// Make API requests with queryString.</span>
  <span class="hljs-built_in">print</span>(queryString);
  <span class="hljs-comment">// ?lastId=&amp;filter[name][Contains][]=mike&amp;filter[name][Contains][]=jessi&amp;sort=name,ascending</span>
  <span class="hljs-keyword">final</span> dataFromServer = <span class="hljs-keyword">await</span> Future.value(<span class="hljs-string">"""
  {
    "isLast": false,
    "data": [
      {
        "id": 1,
        "name": "mike"
      },
      {
        "id": 2,
        "name": "jessi"
      }
    ]
  }
  """</span>);

  <span class="hljs-comment">// Convert the json format received from the server into Map format.</span>
  <span class="hljs-keyword">final</span> parsedData = jsonDecode(dataFromServer);

  <span class="hljs-comment">// Include whether this is the last page when the server responds with data.</span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isLast = parsedData[<span class="hljs-string">'isLast'</span>];

  <span class="hljs-comment">// Create a PlutoRow.</span>
  <span class="hljs-keyword">final</span> rows = parsedData.data.map&lt;PlutoRow&gt;((rowData) {
    <span class="hljs-keyword">return</span> PlutoRow.fromJson(rowData);
  });

  <span class="hljs-comment">// If this is the last page, you can notify the user with a message.</span>
  <span class="hljs-keyword">if</span> (isLast &amp;&amp; mounted) {
    ScaffoldMessenger.of(context).showSnackBar(
      <span class="hljs-keyword">const</span> SnackBar(content: Text(<span class="hljs-string">'Last Page!'</span>)),
    );
  }

  <span class="hljs-keyword">return</span> PlutoInfinityScrollRowsResponse(
    isLast: isLast,
    rows: rows.toList(),
  );
}
</code></pre>
<h3 id="heading-additional-comments-in-common">Additional comments in common</h3>
<ul>
<li>When sorting and filtering events occur, the fetch callback requests the first page.<br /><code>PlutoLazyPagination is page = 1</code><br /><code>PlutoInfinityScrollRows is lastRow = null</code><br />This is because a sorting or filtering event must show the user new data with sorting and filtering applied.<br />In some cases, sorting or filtering can be handled client-side rather than being handled by the server.<br /><code>fetchWithSorting = false, fetchWithFiltering = false</code><br />In this case, it is sorted or filtered based on the currently existing rows.</li>
<li>The existing <code>stateManager.setPage</code> operation is not valid.<br />The way it is handled by the server does not have a separate page state internally in <code>PlutoGrid</code>.<br />You do the paging inside the <code>PlutoLazyPagination</code> or <code>PlutoInfinityScrollRows</code> widget.<br />Therefore, page changes or additions are handled by deleting and inserting all existing rows of <code>PlutoGrid</code>.<br />A separate implementation is required if client-side caching handling is required. This is not yet to be considered, please comment on the GitHub issue and I will consider handling caching.</li>
<li>When data is fetched from the server, it is common that filtering and sorting are not applied to all columns.<br />There are properties that can prevent filtering and sorting of certain columns.<br /><code>enableSorting, enableFilterMenuItem</code> in <code>PlutoColumn</code>.<br />You may also need a single filtering input rather than a filtering input for each column.
In this case, a separate UI is not provided. If necessary, please suggest it to a GitHub issue and I will consider adding it.</li>
<li>The loading screen uses an internal loading widget.<br />A custom loading UI is not yet available. If necessary, please comment on a github issue and I will consider adding it.</li>
<li>When creating a return value by fetching data from the server and creating a <code>PlutoRow</code>, you need to pay attention to the <code>PlutoColumn.field</code> value.<br /> The <code>field</code> passed to <code>columns</code> in <code>PlutoGrid</code> must match.<br /> <code>PlutoRow.cells</code> is of type <code>Map&lt;String, PlutoCell&gt;</code>, and the part corresponding to String should be <code>PlutoColumn.field</code>.</li>
</ul>
<blockquote>
<p>If you have more questions, please contact us through the GitHub issue.
  https://github.com/bosskmk/pluto_grid</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Row grouping]]></title><description><![CDATA[Row grouping is a function to group rows of PlutoGrid.
Group by column
Rows are grouped by grouping cell values ​​based on a specified column.PlutoRows are automatically grouped by cell value without the user having to group them.
// In the onLoaded ...]]></description><link>https://pluto.weblaze.dev/row-grouping</link><guid isPermaLink="true">https://pluto.weblaze.dev/row-grouping</guid><category><![CDATA[row-group]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Sat, 15 Oct 2022 17:11:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665852891673/h3Iw_y20c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Row grouping is a function to group rows of PlutoGrid.</p>
<h3 id="heading-group-by-column">Group by column</h3>
<p>Rows are grouped by grouping cell values ​​based on a specified column.<br />PlutoRows are automatically grouped by cell value without the user having to group them.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// In the onLoaded callback of PlutoGrid, set the delegate as shown below.</span>
onLoaded: (e) =&gt; e.stateManager.setRowGroup(PlutoRowGroupByColumnDelegate(
  columns: [
    e.stateManager.columns[<span class="hljs-number">0</span>],
    e.stateManager.columns[<span class="hljs-number">1</span>],
  ],
  <span class="hljs-comment">// Decide whether to display the expand/collapse buttons in the first cell.</span>
  <span class="hljs-comment">// To set this value to true when grouped by column,</span>
  <span class="hljs-comment">// You need to hide the columns except for the first column.</span>
  <span class="hljs-comment">// stateManager.hideColumns(stateManager.columns[1]);</span>
  <span class="hljs-comment">// If this value is false, expand/collapse buttons are displayed to fit the column depth.</span>
  showFirstExpandableIcon: <span class="hljs-keyword">false</span>,
  <span class="hljs-comment">// Decide whether to display the number of sub-rows of the grouped column in the cell.</span>
  showCount: <span class="hljs-keyword">true</span>,
  <span class="hljs-comment">// Decide whether to display a summary when displaying the number of sub-rows.</span>
  <span class="hljs-comment">// ex) 1,234,567 &gt; 1.2M</span>
  enableCompactCount: <span class="hljs-keyword">true</span>,
)),
</code></pre>
<h3 id="heading-tree-structure-grouping">Tree structure grouping</h3>
<p>Rows are grouped by user-defined criteria.<br />User needs to group PlutoRows and pass them to PlutoGrid.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Set the type of PlutoRow to group as shown below,</span>
<span class="hljs-comment">// Grouping rows should be delivered to rows of PlutoGrid.</span>
<span class="hljs-comment">// The type of every row does not have to be group.</span>
PlutoRow(
  cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'PlutoGrid'</span>)},
  type: PlutoRowType.group(
      children: FilteredList&lt;PlutoRow&gt;(
    initialList: [
      PlutoRow(
        cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'lib'</span>)},
        type: PlutoRowType.group(
          children: FilteredList&lt;PlutoRow&gt;(
            initialList: [
              PlutoRow(
                cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'src'</span>)},
                type: PlutoRowType.group(
                    children: FilteredList&lt;PlutoRow&gt;(
                  initialList: [
                    PlutoRow(cells: {
                      <span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'pluto_grid.dart'</span>)
                    }),
                    PlutoRow(cells: {
                      <span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'pluto_dual_grid.dart'</span>)
                    }),
                  ],
                )),
              ),
            ],
          ),
        ),
      ),
      PlutoRow(
        cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'test'</span>)},
        type: PlutoRowType.group(
          children: FilteredList&lt;PlutoRow&gt;(
            initialList: [
              PlutoRow(
                cells: {
                  <span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'pluto_grid_test.dart'</span>)
                },
              ),
            ],
          ),
        ),
      ),
    ],
  )),
),
PlutoRow(
  cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'PlutoMenuBar'</span>)},
  type: PlutoRowType.group(
      children: FilteredList&lt;PlutoRow&gt;(
    initialList: [
      PlutoRow(
        cells: {<span class="hljs-string">'files'</span>: PlutoCell(value: <span class="hljs-string">'pluto_menu_bar.dart'</span>)},
      ),
    ],
  )),
),

<span class="hljs-comment">// In the onLoaded callback of PlutoGrid, set the delegate as shown below.</span>
onLoaded: (e) =&gt; e.stateManager.setRowGroup(PlutoRowGroupTreeDelegate(
  <span class="hljs-comment">// Determine the depth of the column.</span>
  <span class="hljs-comment">// Basically, the order in which the columns are displayed on the screen is the column depth.</span>
  <span class="hljs-comment">// Pass the settings below as default.</span>
  resolveColumnDepth: (column) =&gt; e.stateManager.columnIndex(column),
  <span class="hljs-comment">// Determines whether the cell's text is visible.</span>
  <span class="hljs-comment">// If true is returned, the text of all cells is shown.</span>
  <span class="hljs-comment">// If you want to hide the text of a specific cell,  </span>
  <span class="hljs-comment">// you can define the return value of the cell with a specific condition as false.</span>
  showText: (cell) =&gt; <span class="hljs-keyword">true</span>,
  <span class="hljs-comment">// Decide whether to display the expand/collapse buttons in the first cell.</span>
  <span class="hljs-comment">// If this value is false, expand/collapse buttons are displayed to fit the column depth.</span>
  showFirstExpandableIcon: <span class="hljs-keyword">true</span>,
  <span class="hljs-comment">// Decide whether to display the number of sub-rows of the grouped column in the cell.</span>
  showCount: <span class="hljs-keyword">true</span>,
  <span class="hljs-comment">// Decide whether to display a summary when displaying the number of sub-rows.</span>
  <span class="hljs-comment">// ex) 1,234,567 &gt; 1.2M</span>
  enableCompactCount: <span class="hljs-keyword">true</span>,
)),
</code></pre>
<h3 id="heading-groupby-columns-dynamically">Groupby columns dynamically</h3>
<p>The example below dynamically processes group-by columns by adding the to Group, unGroup buttons to the right menu of the column.</p>
<pre><code class="lang-dart">PlutoGrid(
  columns: columns,
  rows: rows,
  onLoaded: (PlutoGridOnLoadedEvent event) {
    stateManager = event.stateManager;
    stateManager.setRowGroup(
      <span class="hljs-comment">// First, the columns of the PlutoRowGroupByColumnDelegate </span>
      <span class="hljs-comment">// start with an empty list.</span>
      PlutoRowGroupByColumnDelegate(
        columns: [],
      ),
    );
  },
  <span class="hljs-comment">// Set a custom column menu delegate to add a button to the column menu.</span>
  columnMenuDelegate: _CustomColumnMenuDelegate(),
),

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_CustomColumnMenuDelegate</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PlutoColumnMenuDelegate</span> </span>{
  <span class="hljs-keyword">final</span> PlutoColumnMenuDelegateDefault defaultDelegate =
      <span class="hljs-keyword">const</span> PlutoColumnMenuDelegateDefault();

  <span class="hljs-built_in">bool</span> isGroupColumn(PlutoGridStateManager stateManager, PlutoColumn column) {
    <span class="hljs-keyword">return</span> (stateManager.rowGroupDelegate <span class="hljs-keyword">as</span> PlutoRowGroupByColumnDelegate)
        .columns
        .contains(column);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">List</span>&lt;PopupMenuEntry&lt;<span class="hljs-built_in">dynamic</span>&gt;&gt; buildMenuItems({
    <span class="hljs-keyword">required</span> PlutoGridStateManager stateManager,
    <span class="hljs-keyword">required</span> PlutoColumn column,
  }) {
    <span class="hljs-keyword">final</span> Color textColor = stateManager.style.cellTextStyle.color!;

    <span class="hljs-keyword">final</span> groupColumn = isGroupColumn(stateManager, column);

    <span class="hljs-keyword">return</span> [
      ...defaultDelegate.buildMenuItems(
          stateManager: stateManager, column: column),
      PopupMenuItem&lt;<span class="hljs-built_in">dynamic</span>&gt;(
        value: groupColumn ? _CustomMenuItem.unGroup : _CustomMenuItem.toGroup,
        height: <span class="hljs-number">36</span>,
        enabled: <span class="hljs-keyword">true</span>,
        child: Text(
          groupColumn ? <span class="hljs-string">'unGroup'</span> : <span class="hljs-string">'toGroup'</span>,
          style: TextStyle(
            color: textColor,
            fontSize: <span class="hljs-number">13</span>,
          ),
        ),
      ),
    ];
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> onSelected({
    <span class="hljs-keyword">required</span> BuildContext context,
    <span class="hljs-keyword">required</span> PlutoGridStateManager stateManager,
    <span class="hljs-keyword">required</span> PlutoColumn column,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> mounted,
    <span class="hljs-keyword">required</span> selected,
  }) {
    <span class="hljs-keyword">if</span> (selected <span class="hljs-keyword">is</span> PlutoGridColumnMenuItem) {
      defaultDelegate.onSelected(
        context: context,
        stateManager: stateManager,
        column: column,
        mounted: mounted,
        selected: selected,
      );
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (selected <span class="hljs-keyword">is</span> _CustomMenuItem) {
      <span class="hljs-keyword">switch</span> (selected) {
        <span class="hljs-keyword">case</span> _CustomMenuItem.toGroup:
          stateManager.setRowGroup(PlutoRowGroupByColumnDelegate(columns: [
            ...(stateManager.rowGroupDelegate <span class="hljs-keyword">as</span> PlutoRowGroupByColumnDelegate)
                .columns,
            column,
          ]));
          stateManager.updateVisibilityLayout();
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> _CustomMenuItem.unGroup:
          stateManager.setRowGroup(PlutoRowGroupByColumnDelegate(columns: [
            ...(stateManager.rowGroupDelegate <span class="hljs-keyword">as</span> PlutoRowGroupByColumnDelegate)
                .columns
                .where((e) =&gt; e.field != column.field),
          ]));
          stateManager.updateVisibilityLayout();
          <span class="hljs-keyword">break</span>;
      }
    }
  }
}

<span class="hljs-keyword">enum</span> _CustomMenuItem {
  toGroup,
  unGroup,
}
</code></pre>
<h3 id="heading-bottom-aggregate-column-in-grouping-state">Bottom aggregate column in grouping state</h3>
<p>https://weblaze.dev/pluto_grid/build/web/#feature/column-footer<br />When adding an aggregate column at the bottom of the link above, you can set the groupedRowType property to set the conditions for the rows to be included in the aggregation in the grouping state.</p>
<pre><code class="lang-dart">columns[<span class="hljs-number">0</span>].footerRenderer = (c) {
  <span class="hljs-keyword">return</span> PlutoAggregateColumnFooter(
    rendererContext: c,
    type: PlutoAggregateColumnType.count,
    <span class="hljs-comment">// The groupedRowType property takes effect when rows are grouped.</span>
    groupedRowType: PlutoAggregateColumnGroupedRowType.rows,
    format: <span class="hljs-string">'CheckedCount : #,###'</span>,
    alignment: Alignment.center,
    filter: (cell) =&gt; cell.row.checked == <span class="hljs-keyword">true</span>,
  );
};

<span class="hljs-comment">// Include all groups and rows in the aggregation.</span>
PlutoAggregateColumnGroupedRowType.all
<span class="hljs-comment">// Include groups and rows excluding collapsed sub-rows in the aggregation.</span>
PlutoAggregateColumnGroupedRowType.expandedAll
<span class="hljs-comment">// Include rows excluding group rows in the aggregation.</span>
PlutoAggregateColumnGroupedRowType.rows
<span class="hljs-comment">// Include expanded rows excluding group rows in the aggregate.</span>
PlutoAggregateColumnGroupedRowType.expandedRows
</code></pre>
<h3 id="heading-restrictions">Restrictions</h3>
<ul>
<li>In version 5.2.0, the drag function of the row in the grouping state is not yet supported.</li>
<li>If a row is added when grouped by column, if there is a duplicate value according to the cell value of the row being added, it is included in the existing group. Otherwise, a new group is added.</li>
</ul>
<h3 id="heading-web-demo-link">Web demo link</h3>
<p>https://weblaze.dev/pluto_grid/build/web/#feature/row-group</p>
]]></content:encoded></item><item><title><![CDATA[PlutoLayout Live Demo]]></title><description><![CDATA[PlutoLayout Live Demo
A link to a live demo where you can see PlutoLayout in action on the web.]]></description><link>https://pluto.weblaze.dev/plutolayout-live-demo</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutolayout-live-demo</guid><category><![CDATA[tabview]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[layout]]></category><category><![CDATA[UI]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Thu, 13 Oct 2022 10:56:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665658504987/zhonOVqij.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://weblaze.dev/pluto_layout/build/web/#/">PlutoLayout Live Demo</a></p>
<p>A link to a live demo where you can see PlutoLayout in action on the web.</p>
]]></content:encoded></item><item><title><![CDATA[PlutoGrid - Development News - Row grouping]]></title><description><![CDATA[I'm currently developing a grouping function of rows for version 5.2, which is the next version of PlutoGrid 5.1.
[Done] Grouping of rows structured by columns


Group multiple rows by column. (2 columns in the image)
Dynamically change or ungroup gr...]]></description><link>https://pluto.weblaze.dev/plutogrid-development-news-row-grouping</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutogrid-development-news-row-grouping</guid><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[datatable]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[GroupRows]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 26 Sep 2022 00:27:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664156589342/ERdUL9gTj.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I'm currently developing a grouping function of rows for version 5.2, which is the next version of PlutoGrid 5.1.</p>
<h4 id="heading-done-grouping-of-rows-structured-by-columns">[Done] Grouping of rows structured by columns</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149052468/u6xg54kAT.gif" alt="pluto_grid_row_group_01.gif" /></p>
<ul>
<li>Group multiple rows by column. (2 columns in the image)</li>
<li>Dynamically change or ungroup grouping status</li>
<li>Add or delete rows while grouped (multi-select possible)</li>
</ul>
<h4 id="heading-done-sort-in-grouped-state">[Done] Sort in grouped state</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149176963/CWwGDgl25.gif" alt="pluto_grid_row_group_02.gif" /></p>
<ul>
<li>Sort rows while maintaining group status</li>
</ul>
<h4 id="heading-done-filtering-in-grouped-state">[Done] Filtering in grouped state</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149219595/NhwbQnRU3.gif" alt="pluto_grid_row_group_03.gif" /></p>
<ul>
<li>Filter rows while maintaining group status</li>
</ul>
<h4 id="heading-done-pagination-in-grouped-state">[Done] Pagination in grouped state</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149246606/RRbzusKnh.gif" alt="pluto_grid_row_group_04.gif" /></p>
<ul>
<li>Pagination while maintaining group status</li>
</ul>
<h4 id="heading-development-drag-and-move-rows-in-a-grouped-state">[Development] Drag and move rows in a grouped state</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149313887/9o6Hi5sHJ.gif" alt="pluto_grid_row_group_05.gif" />
As in the image above, in the previous version, drag the selected row to move it.
However, complex planning needs to be considered when trying to select and drag a specific row in a grouped state as shown in the image below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664149306643/qxK2kikuo.gif" alt="pluto_grid_row_group_06.gif" />
As in the image above, when the user tries to move selected specific rows by dragging, the row must be moved according to the grouped state.</p>
<h5 id="heading-movement-of-selected-rows-in-a-grouped-state">Movement of selected rows in a grouped state</h5>
<blockquote>
<p>Move all children to another group</p>
<ul>
<li>parent should be deleted</li>
</ul>
<p>Move grouped rows to another parent group</p>
<ul>
<li>If the parent group to be moved already has a group with the same key, there is a key conflict problem.
The children of the group to be moved must be added as children of an existing group.</li>
</ul>
<p>Group row discards children and moves to another location</p>
<ul>
<li>The children of the group must also move along with the group. (UI considerations as to whether or not to select all children when a group row is selected)</li>
</ul>
<p>Group row moves to non-group row position</p>
<ul>
<li>After moving the children of the group row, the group must be deleted.</li>
</ul>
</blockquote>
<p>These problems are possible enough for development, but it will be necessary to plan the UI so that there is no confusion such as a group being deleted or an unselected row being moved when a row is moved from the user's point of view.</p>
<p>If you have a good, simple idea, please comment on my GitHub issue.
https://github.com/bosskmk/pluto_grid/issues</p>
<p>If this problem is not easily solved with the function of moving the selected row in a grouped state, I plan to distribute only this part in the next version.</p>
]]></content:encoded></item><item><title><![CDATA[Add and remove columns and rows.]]></title><description><![CDATA[After setting columns and rows in the constructor of PlutoGrid, how to add columns and rows during runtime and precautions are described.
Setting up columns and rows when creating a grid
PlutoGrid can set columns and rows in the constructor when crea...]]></description><link>https://pluto.weblaze.dev/add-and-remove-columns-and-rows</link><guid isPermaLink="true">https://pluto.weblaze.dev/add-and-remove-columns-and-rows</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Fri, 15 Jul 2022 07:01:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657868433095/stVIei7Op.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After setting columns and rows in the constructor of <code>PlutoGrid</code>, how to add columns and rows during runtime and precautions are described.</p>
<h3 id="heading-setting-up-columns-and-rows-when-creating-a-grid">Setting up columns and rows when creating a grid</h3>
<p><code>PlutoGrid</code> can set columns and rows in the constructor when creating the first grid as follows.</p>
<blockquote>
<p>Note: Do not set columns and rows in the <code>build</code> method when setting columns and rows for the first time.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">DemoScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns = [];

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows = [];

  <span class="hljs-keyword">late</span> PlutoGridStateManager stateManager;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();

    columns.addAll([
      PlutoColumn(
        title: <span class="hljs-string">'name'</span>,
        field: <span class="hljs-string">'name_field'</span>,
        type: PlutoColumnType.text(),
      ),
      PlutoColumn(
        title: <span class="hljs-string">'age'</span>,
        field: <span class="hljs-string">'age_field'</span>,
        type: PlutoColumnType.number(),
      ),
    ]);

    rows.addAll([
      PlutoRow(cells: {
        <span class="hljs-string">'name_field'</span>: PlutoCell(value: <span class="hljs-string">'Mike.'</span>),
        <span class="hljs-string">'age_field'</span>: PlutoCell(value: <span class="hljs-number">20</span>),
      }),
      PlutoRow(cells: {
        <span class="hljs-string">'name_field'</span>: PlutoCell(value: <span class="hljs-string">'John.'</span>),
        <span class="hljs-string">'age_field'</span>: PlutoCell(value: <span class="hljs-number">21</span>),
      }),
    ]);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-comment">// Do not add columns or rows here.</span>
    <span class="hljs-keyword">return</span> Scaffold(
      body: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
        child: PlutoGrid(
          columns: columns,
          rows: rows,
        ),
      ),
    );
  }
}
</code></pre>
<h3 id="heading-add-or-remove-columns-and-rows-after-grid-creation">Add or remove columns and rows after grid creation</h3>
<p>After columns and rows are set in the <code>PlutoGrid</code> constructor, to add or delete columns or rows, you have to do it through <code>PlutoGridStateManager</code>.</p>
<p><code>PlutoGridStateManager</code> can be delivered through <code>onLoaded</code> callback function when <code>PlutoGrid</code> is created.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> PlutoGridStateManager stateManager;

<span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
  <span class="hljs-keyword">return</span> Scaffold(
    body: Container(
      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
      child: PlutoGrid(
        columns: columns,
        rows: rows,
        onLoaded: (event) =&gt; stateManager = event.stateManager,
      ),
    ),
  );
}
</code></pre>
<p>Through the received <code>PlutoGridStateManager</code>, columns or rows can be added when a callback function such as <code>onTap</code> or <code>stream.listen</code> of the button is called.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
Widget build(BuildContext context) {
  <span class="hljs-keyword">return</span> Scaffold(
    body: Column(
      children: [
        TextButton(
          onPressed: () {
            <span class="hljs-comment">// Be careful not to duplicate field names when adding columns.</span>
            <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> lastIndex = stateManager.refColumns.originalList.length;

            stateManager.insertColumns(<span class="hljs-number">0</span>, [
              PlutoColumn(
                title: <span class="hljs-string">'new column <span class="hljs-subst">$lastIndex</span>'</span>,
                field: <span class="hljs-string">'new_field_<span class="hljs-subst">$lastIndex</span>'</span>,
                type: PlutoColumnType.text(),
              )
            ]);
          },
          child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Add column'</span>),
        ),
        Expanded(
          child: Container(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
            child: PlutoGrid(
              columns: columns,
              rows: rows,
              onLoaded: (event) =&gt; stateManager = event.stateManager,
            ),
          ),
        ),
      ],
    ),
  );
}
</code></pre>
<h3 id="heading-methods-related-to-adding-or-removing-columns-and-rows">Methods related to adding or removing columns and rows</h3>
<p>The methods related to adding and removing columns and rows of <code>PlutoGridStateManager</code> are as follows.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Add `columns` in place of `columnIdx`.</span>
<span class="hljs-keyword">void</span> insertColumns(<span class="hljs-built_in">int</span> columnIdx, <span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns);

<span class="hljs-comment">// Remove all `columns`.</span>
<span class="hljs-keyword">void</span> removeColumns(<span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns);

<span class="hljs-comment">// Add `rows` in place of `rowIdx`.</span>
<span class="hljs-keyword">void</span> insertRows(
    <span class="hljs-built_in">int</span> rowIdx,
    <span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows, {
    <span class="hljs-built_in">bool</span> notify = <span class="hljs-keyword">true</span>,
  });

<span class="hljs-comment">// Add new rows of length `count` before the first row.</span>
<span class="hljs-keyword">void</span> prependNewRows({
  <span class="hljs-built_in">int</span> count = <span class="hljs-number">1</span>,
});

<span class="hljs-comment">// Add `rows` to the beginning of the first row.</span>
<span class="hljs-keyword">void</span> prependRows(<span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows);

<span class="hljs-comment">// Add new rows of length `count` after the last row.</span>
<span class="hljs-keyword">void</span> appendNewRows({
  <span class="hljs-built_in">int</span> count = <span class="hljs-number">1</span>,
});

<span class="hljs-comment">// Add `rows` after the last row.</span>
<span class="hljs-keyword">void</span> appendRows(<span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows);

<span class="hljs-comment">// Remove the currently focused row.</span>
<span class="hljs-keyword">void</span> removeCurrentRow();

<span class="hljs-comment">// Remove `rows` Remove all.</span>
<span class="hljs-keyword">void</span> removeRows(<span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows);

<span class="hljs-comment">// Remove all rows.</span>
<span class="hljs-keyword">void</span> removeAllRows({<span class="hljs-built_in">bool</span> notify = <span class="hljs-keyword">true</span>});
</code></pre>
<h3 id="heading-how-to-asynchronously-append-a-large-number-of-rows">How to asynchronously append a large number of rows</h3>
<p>If the number of rows to be added at once is large, the UI may freeze due to the load on the CPU operation.
In this case, the CPU operation can be processed asynchronously.</p>
<p>Below is a link to a demo page that works on the web. You can also check the example source code by tapping the <code>Source</code> button on the demo page.</p>
<p><a target="_blank" href="https://weblaze.dev/pluto_grid/build/web/#feature/add-rows-asynchronously">Add rows asynchronously</a></p>
]]></content:encoded></item><item><title><![CDATA[Configuration]]></title><description><![CDATA[When creating a PlutoGrid, you can change the grid settings by passing PlutoGridConfiguration to the configuration property.
child: PlutoGrid(
  columns: columns,
  rows: rows,
  configuration: const PlutoGridConfiguration(),
),

enableMoveDownAfterS...]]></description><link>https://pluto.weblaze.dev/configuration</link><guid isPermaLink="true">https://pluto.weblaze.dev/configuration</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[datatable]]></category><category><![CDATA[configuration]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Tue, 12 Jul 2022 07:08:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657599901603/UZ_kMgOn8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When creating a <code>PlutoGrid</code>, you can change the grid settings by passing <code>PlutoGridConfiguration</code> to the <code>configuration</code> property.</p>
<pre><code class="lang-dart">child: PlutoGrid(
  columns: columns,
  rows: rows,
  configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(),
),
</code></pre>
<h4 id="heading-enablemovedownafterselecting">enableMoveDownAfterSelecting</h4>
<p>After a value is selected in the date, time, or selection type popup, the current cell position of the grid is moved down after the popup is closed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657600595850/ngVVlA2jw.gif" alt="Animation.gif" /></p>
<h4 id="heading-enablemovehorizontalinediting">enableMoveHorizontalInEditing</h4>
<p>If the value is set to <code>true</code>, if you press the direction key once more after reaching the end of the left and right text in the edit state, the current cell moves in the entered direction.<br />If the value is set to <code>false</code>, the current cell cannot be moved with the left and right arrow keys in the editing state.<br />If the cell is not in edit state, the current cell can be moved left or right regardless of this value.</p>
<h4 id="heading-enterkeyaction">enterKeyAction</h4>
<p>Sets the behavior of the Enter key when the current cell is selected.</p>
<ul>
<li><code>PlutoEnterKeyAction.editingAndMoveDown</code> switches the current cell to the edit state when the enter key is pressed if the current cell is not in the edit state.<br />If you press the Enter key once more in the edit state, the current cell position is moved to the cell below.</li>
<li><code>PlutoEnterKeyAction.editingAndMoveRight</code> converts the current cell to the edit state when the enter key is pressed if the current cell is not in the edit state.<br />If you press the Enter key once more in the edit state, the current cell position is moved to the right.</li>
<li><code>PlutoEnterKeyAction.toggleEditing</code> toggles the editing status of the current cell when the Enter key is pressed, and does not move the current cell position.</li>
<li><code>PlutoGridEnterKeyAction.none</code> does not perform any action when the enter key is pressed.</li>
</ul>
<h4 id="heading-style">style</h4>
<p>This is the style setting for <code>PlutoGrid</code>.</p>
<pre><code class="lang-dart">configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(
  style: PlutoGridStyleConfig(
    enableGridBorderShadow: <span class="hljs-keyword">true</span>,
  ),
),
</code></pre>
<h5 id="heading-options-of-style">Options of style</h5>
<p><code>enableGridBorderShadow</code> enables shadowing on the grid's borders.  </p>
<p><code>enableColumnBorderVertical</code> enables vertical dividing borders between columns.</p>
<p><code>enableColumnBorderHorizontal</code> enables horizontal borders for columns.</p>
<p><code>enableCellBorderVertical</code> enables cell-to-cell vertical separator borders.</p>
<p><code>enableCellBorderHorizontal</code> enables horizontal boarding of cells.</p>
<p><code>enableRowColorAnimation</code> enables the animation effect of the background color of a row when the current row position is moved.</p>
<h5 id="heading-colors-of-style">Colors of style</h5>
<p><code>gridBackgroundColor</code> sets the background color of the grid.</p>
<p><code>rowColor</code> sets the background color of the row.</p>
<p><code>oddRowColor</code> sets the background color of the odd-numbered row.</p>
<p><code>evenRowColor</code> sets the background color of the even-numbered row.</p>
<p><code>activatedColor</code> sets the background color of the currently focused row or cell.</p>
<p><code>checkedColor</code> sets the background color of the selected row.</p>
<p><code>cellColorInEditState</code> sets the background color of the cell when the cell is in edit state.</p>
<p><code>cellColorInReadOnlyState</code> sets the background color when the cell is in the edit state and the cell is a read-only cell.(PlutoColumn.readonly)</p>
<p><code>dragTargetColumnColor</code> sets the background color of the column to be dragged when the column is moved by dragging it.<br />If it is impossible to move to the position of the column that is the drag target, the background color of the column of the drag target does not change.<br />(In the case of a fixed column, if the overall width is narrow, the position is limited)</p>
<p><code>iconColor</code> sets the color of the icon used by default inside the grid.</p>
<p><code>disabledIconColor</code> sets the color of the icon's disabled state.</p>
<p><code>menuBackgroundColor</code> sets the background color of the menu popup on the right side of the column.</p>
<p><code>gridBorderColor</code> sets the border color of the grid.</p>
<p><code>borderColor</code> sets the border color of columns, column groups, cells, and rows.</p>
<p><code>activatedBorderColor</code> sets the border color when a row or cell receives focus.</p>
<p><code>inactivatedBorderColor</code> sets the border color when a row or cell loses focus.</p>
<h5 id="heading-sizes-of-style">Sizes of style</h5>
<p><code>iconSize</code> sets the size of the icon used by default inside the grid.</p>
<p><code>rowHeight</code> sets the height of the row.</p>
<p><code>columnHeight</code> sets the height of the column.</p>
<p><code>columnFilterHeight</code> sets the height of the column filter.</p>
<p><code>defaultColumnTitlePadding</code> sets the padding of the column title area.</p>
<p><code>defaultColumnFilterPadding</code> sets the padding of the column filter area.</p>
<p><code>defaultCellPadding</code> sets the padding of the cell.</p>
<h5 id="heading-textstyle-of-style">TextStyle of style</h5>
<p><code>columnTextStyle</code> sets the text style of the column.</p>
<p><code>cellTextStyle</code> sets the text style of the cell.</p>
<h5 id="heading-icons-of-style">Icons of style</h5>
<p><code>columnContextIcon</code> sets the menu icon to the right of the column title.</p>
<p><code>columnResizeIcon</code> sets the resize icon to the right of the column title. (In case PlutoColumn.enableContextMenu is <code>false</code>.)</p>
<h5 id="heading-border-radius-of-style">Border radius of style</h5>
<p><code>gridBorderRadius</code> sets the border radius of the grid.</p>
<p><code>gridPopupBorderRadius</code> sets the border radius of the popup inside the grid.</p>
<h4 id="heading-scrollbar">scrollbar</h4>
<p>It is the horizontal and vertical scroll bar setting of the grid.</p>
<pre><code class="lang-dart">configuration: PlutoGridConfiguration(
  scrollbar: <span class="hljs-keyword">const</span> PlutoGridScrollbarConfig(
    isAlwaysShown: <span class="hljs-keyword">false</span>,
    scrollbarThickness: <span class="hljs-number">8</span>,
    scrollbarThicknessWhileDragging: <span class="hljs-number">10</span>,
  ),
),
</code></pre>
<p><code>draggableScrollbar</code> is whether the scrollbar can be scrolled by dragging it.</p>
<p><code>isAlwaysShown</code> determines whether the scrollbar will remain visible.</p>
<p><code>scrollbarRadius</code> sets the radius of the scrollbar.</p>
<p><code>scrollbarRadiusWhileDragging</code> sets the radius while dragging the scrollbar.</p>
<p><code>scrollbarThickness</code> sets the thickness of the scrollbar.</p>
<p><code>scrollbarThicknessWhileDragging</code> sets the thickness when the scrollbar is being dragged.</p>
<h4 id="heading-columnfilter">columnFilter</h4>
<p>This is the filter setting for the column.<br />If you press <code>F3</code> while the cell is in focus, the focus is moved to the filter input box of the column of the current cell.<br />If you press the <code>F3</code> key again in the filter input box, a filtering pop-up is called where you can set the entire filtering.<br />If you press <code>ESC</code> in the filter input box, it will return to the previous cell position.<br />If you enter the <code>arrow down</code> key in the filter input box, it moves to the first cell of the column.  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657604399935/wUlbkOsCg.png" alt="column_filter.png" /></p>
<pre><code class="lang-dart">configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(
  columnFilter: PlutoGridColumnFilterConfig(),
),
</code></pre>
<h5 id="heading-default-column-filter-type">Default column filter type</h5>
<p>The built-in column filters are set in the <code>FilterHelper.defaultFilters</code> static property.
If no <code>columnFilter</code> is set, a default filter is provided.</p>
<ul>
<li><p><code>PlutoFilterTypeContains</code> checks whether the filter input value is included in the values ​​of the cells of the corresponding column.<br /><code>RegExp : /filter input value/</code></p>
</li>
<li><p><code>PlutoFilterTypeEquals</code> checks whether the filter input value matches the values ​​of the cells of the corresponding column.<br /><code>RegExp : /^Filter input value$/</code></p>
</li>
<li><p><code>PlutoFilterTypeStartsWith</code> checks whether the filter input value matches the value of the cells in the corresponding column.<br /><code>RegExp : /^Filter input value/</code></p>
</li>
<li><p><code>PlutoFilterTypeEndsWith</code> means that the filter input value is the cell of the corresponding column.<br /><code>RegExp : /Filter input value$/</code></p>
</li>
<li><p><code>PlutoFilterTypeGreaterThan</code> checks whether the filter input value is greater than the value of the cells in the corresponding column.<br />According to the definition of the <code>PlutoColumn.type.compare</code> function.</p>
</li>
<li><p><code>PlutoFilterTypeGreaterThanOrEqualTo</code> checks whether the filter input value is greater than or equal to the value of the cells in the corresponding column.<br />According to the definition of the <code>PlutoColumn.type.compare</code> function.</p>
</li>
<li><p><code>PlutoFilterTypeLessThan</code> checks whether the filter input value is less than the value of the cells of the corresponding column.<br />According to the definition of the <code>PlutoColumn.type.compare</code> function.</p>
</li>
<li><p><code>PlutoFilterTypeLessThanOrEqualTo</code> checks whether the filter input value is less than or equal to the value of the cells of the corresponding column.<br />According to the definition of the <code>PlutoColumn.type.compare</code> function.</p>
</li>
</ul>
<h5 id="heading-user-defined-column-filters">User-Defined Column Filters</h5>
<p>The example code below applies different filters depending on the <code>PlutoColumn.field</code> .</p>
<pre><code class="lang-dart">columnFilter: PlutoGridColumnFilterConfig(
  filters: <span class="hljs-keyword">const</span> [
    ...FilterHelper.defaultFilters,
    <span class="hljs-comment">// custom filter</span>
    ClassYouImplemented(),
  ],
  resolveDefaultColumnFilter: (column, resolver) {
    <span class="hljs-keyword">if</span> (column.field == <span class="hljs-string">'text'</span>) {
      <span class="hljs-keyword">return</span> resolver&lt;PlutoFilterTypeContains&gt;() <span class="hljs-keyword">as</span> PlutoFilterType;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (column.field == <span class="hljs-string">'number'</span>) {
      <span class="hljs-keyword">return</span> resolver&lt;PlutoFilterTypeGreaterThan&gt;()
          <span class="hljs-keyword">as</span> PlutoFilterType;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (column.field == <span class="hljs-string">'date'</span>) {
      <span class="hljs-keyword">return</span> resolver&lt;PlutoFilterTypeLessThan&gt;() <span class="hljs-keyword">as</span> PlutoFilterType;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (column.field == <span class="hljs-string">'select'</span>) {
      <span class="hljs-keyword">return</span> resolver&lt;ClassYouImplemented&gt;() <span class="hljs-keyword">as</span> PlutoFilterType;
    }

    <span class="hljs-keyword">return</span> resolver&lt;PlutoFilterTypeContains&gt;() <span class="hljs-keyword">as</span> PlutoFilterType;
  },),

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassYouImplemented</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PlutoFilterType</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> title =&gt; <span class="hljs-string">'Custom contains'</span>;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">get</span> compare =&gt; ({
        <span class="hljs-keyword">required</span> <span class="hljs-built_in">String?</span> base,
        <span class="hljs-keyword">required</span> <span class="hljs-built_in">String?</span> search,
        <span class="hljs-keyword">required</span> PlutoColumn? column,
      }) {
        <span class="hljs-keyword">var</span> keys = search!.split(<span class="hljs-string">','</span>).map((e) =&gt; e.toUpperCase()).toList();

        <span class="hljs-keyword">return</span> keys.contains(base!.toUpperCase());
      };

  <span class="hljs-keyword">const</span> ClassYouImplemented();
}
</code></pre>
<p>The <code>ClassYouImplemented</code> class that implements <code>PlutoFilterType</code> filters whether multiple options are included by separating the filter input value with <code>,</code> in case of a select type column.
If the column has the <code>select</code> option <code>['one', 'two', 'three']</code>
If you enter the filter input value as <code>one, two</code>, cases containing <code>one</code> or <code>two</code> will be filtered.</p>
<h4 id="heading-columnsize">columnSize</h4>
<p>You can change the column width automatically or set the column width adjustment condition.</p>
<pre><code class="lang-dart">configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(
  columnSize: PlutoGridColumnSizeConfig(),
),
</code></pre>
<h5 id="heading-autosizemode">autoSizeMode</h5>
<p>Automatically change the width of a column in situations such as when the grid starts or the width of the grid changes.</p>
<p>If the total width of the grid is insufficient and all columns reach the minimum width, horizontal scrolling may occur because the total width of the column becomes larger than the entire grid.</p>
<p>If <code>autoSizeMode</code> is applied in a state other than <code>none</code>, if <code>resizeMode</code> is also applied as <code>PlutoResizeMode.pushAndPull</code>, the width of columns can be changed not to exceed the total width of the grid.</p>
<p><code>PlutoAutoSizeMode.none</code> disables auto change of width.</p>
<p><code>PlutoAutoSizeMode.equal</code> changes all columns to the same width.</p>
<p><code>PlutoAutoSizeMode.scale</code> changes the column width proportionally according to the current width of each column.</p>
<p>In <code>autoSizeMode</code>, there is an option to set whether <code>autoSizeMode</code> is applied again according to the state of a column being moved, hidden or unhidden, etc.</p>
<ul>
<li><p><code>restoreAutoSizeAfterHideColumn</code> determines whether <code>autoSizeMode</code> is reapplied after a column is hidden or unhidden.</p>
</li>
<li><p><code>restoreAutoSizeAfterFrozenColumn</code> determines whether <code>autoSizeMode</code> is reapplied after the column's frozen state is changed.</p>
</li>
<li><p><code>restoreAutoSizeAfterMoveColumn</code> determines whether <code>autoSizeMode</code> is reapplied after a column is moved.</p>
</li>
<li><p><code>restoreAutoSizeAfterInsertColumn</code> determines whether <code>autoSizeMode</code> is applied again after a new column is added.</p>
</li>
<li><p><code>restoreAutoSizeAfterRemoveColumn</code> determines whether <code>autoSizeMode</code> is reapplied after a column is deleted.</p>
</li>
</ul>
<h5 id="heading-resizemode">resizeMode</h5>
<p>Set the condition to change the width of the column.</p>
<p><code>PlutoResizeMode.none</code> disables changing the column width.</p>
<p>In <code>PlutoResizeMode.normal</code>, when the width of a column is changed, only the width of the corresponding column is changed.<br />In this case, the overall scroll width of the grid is also widened or narrowed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657606685038/huMFP76DB.gif" alt="resize_normal.gif" /></p>
<p><code>PlutoResizeMode.pushAndPull</code> operates to change the width of the corresponding column within the entire grid width, and to widen or narrow the width of the surrounding columns by that much.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657606693604/wV79u_Sp9.gif" alt="resize_push_and_pull.gif" /></p>
<h4 id="heading-localetext">localeText</h4>
<p>Change the language of the characters used inside the grid.
If the value is not set, it is set to English.</p>
<h5 id="heading-built-in-languages">Built-in languages</h5>
<p>English <code>PlutoGridLocaleText()</code><br />Chinese <code>PlutoGridLocaleText.china()</code><br />Korean <code>PlutoGridLocaleText.korean()</code><br />Russian <code>PlutoGridLocaleText.russian()</code><br />Czech <code>PlutoGridLocaleText.czech()</code><br />brazilianPortuguese <code>PlutoGridLocaleText.brazilianPortuguese()</code><br />Spanish <code>PlutoGridLocaleText.spanish()</code><br />Persian <code>PlutoGridLocaleText.persian()</code><br />Arabic <code>PlutoGridLocaleText.arabic()</code>  </p>
<h5 id="heading-change-the-default-language-set">Change the default language set</h5>
<pre><code class="lang-dart">configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(
  localeText: PlutoGridLocaleText(
    unfreezeColumn: <span class="hljs-string">'Unpin column'</span>,
    freezeColumnToStart: <span class="hljs-string">'Pin column to left'</span>,
  ),
),
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Introduction to PlutoMenuBar]]></title><description><![CDATA[PlutoMenuBar is a menu bar that can add multiple sub menus. Works on all platforms supported by Flutter.

If there is a submenu, an arrow icon is displayed to the right of the menu item.
Tapping on a menu item with this submenu brings up the followin...]]></description><link>https://pluto.weblaze.dev/introduction-to-plutomenubar</link><guid isPermaLink="true">https://pluto.weblaze.dev/introduction-to-plutomenubar</guid><category><![CDATA[Flutter]]></category><category><![CDATA[MenuBar]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Tue, 12 Jul 2022 04:08:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657597948152/BIbFdIa2A.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PlutoMenuBar is a menu bar that can add multiple sub menus. Works on all platforms supported by Flutter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657598180227/eY8ff8tb4.png" alt="menubar1.png" /></p>
<p>If there is a submenu, an arrow icon is displayed to the right of the menu item.
Tapping on a menu item with this submenu brings up the following submenus.
To return to the previous menu, simply tap the ‘Go Back’ menu item.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657598252278/5mzfN_Srd.png" alt="menubar2.png" /></p>
<h4 id="heading-type-of-menu-item">Type of menu item</h4>
<ul>
<li><code>PlutoMenuItem</code> is a basic button type menu item.</li>
<li><code>PlutoMenuItem.checkbox</code> is the menu item that the checkbox widget appears.</li>
<li><code>PlutoMenuItem.radio</code> is the menu item where the radio button widget appears.</li>
</ul>
<h4 id="heading-set-submenu">Set submenu</h4>
<p><code>PlutoMenuItem</code> can pass <code>children</code> attribute.
<code>children</code> is a <code>List&lt;PlutoMenuItem&gt;?</code> type, and the same <code>PlutoMenuItem</code> can be set as a submenu.
If the <code>children</code> value is not passed, it acts as a menu item with no submenu.</p>
<h4 id="heading-full-sample-code">Full sample code</h4>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
      home: Scaffold(
        appBar: AppBar(
          title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'PlutoMenuBar'</span>),
        ),
        body: <span class="hljs-keyword">const</span> PlutoMenuBarDemo(),
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PlutoMenuBarDemo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> PlutoMenuBarDemo({
    <span class="hljs-keyword">super</span>.key,
  });

  <span class="hljs-keyword">void</span> message(context, <span class="hljs-built_in">String</span> text) {
    ScaffoldMessenger.of(context).hideCurrentSnackBar();

    <span class="hljs-keyword">final</span> snackBar = SnackBar(
      content: Text(text),
    );

    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  <span class="hljs-built_in">List</span>&lt;PlutoMenuItem&gt; getMenus(BuildContext context) {
    <span class="hljs-keyword">return</span> [
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 1'</span>,
        icon: Icons.home,
        children: [
          PlutoMenuItem(
            title: <span class="hljs-string">'Menu 1-1'</span>,
            icon: Icons.group,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-1 tap'</span>),
            children: [
              PlutoMenuItem(
                title: <span class="hljs-string">'Menu 1-1-1'</span>,
                onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-1-1 tap'</span>),
                children: [
                  PlutoMenuItem(
                    title: <span class="hljs-string">'Menu 1-1-1-1'</span>,
                    onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-1-1-1 tap'</span>),
                  ),
                  PlutoMenuItem(
                    title: <span class="hljs-string">'Menu 1-1-1-2'</span>,
                    onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-1-1-2 tap'</span>),
                  ),
                ],
              ),
              PlutoMenuItem(
                title: <span class="hljs-string">'Menu 1-1-2'</span>,
                onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-1-2 tap'</span>),
              ),
            ],
          ),
          PlutoMenuItem(
            title: <span class="hljs-string">'Menu 1-2'</span>,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-2 tap'</span>),
          ),
          PlutoMenuItem.checkbox(
            title: <span class="hljs-string">'Menu 1-3'</span>,
            initialCheckValue: <span class="hljs-keyword">true</span>,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-3 tap'</span>),
            onChanged: (flag) =&gt; <span class="hljs-built_in">print</span>(flag),
          ),
          PlutoMenuItem.radio(
            title: <span class="hljs-string">'Menu 1-3'</span>,
            initialRadioValue: _RadioItems.one,
            radioItems: _RadioItems.values,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 1-3 tap'</span>),
            onChanged: (item) =&gt; <span class="hljs-built_in">print</span>(item),
            getTitle: (item) {
              <span class="hljs-keyword">switch</span> (item <span class="hljs-keyword">as</span> _RadioItems) {
                <span class="hljs-keyword">case</span> _RadioItems.one:
                  <span class="hljs-keyword">return</span> <span class="hljs-string">'One'</span>;
                <span class="hljs-keyword">case</span> _RadioItems.two:
                  <span class="hljs-keyword">return</span> <span class="hljs-string">'Two'</span>;
                <span class="hljs-keyword">case</span> _RadioItems.three:
                  <span class="hljs-keyword">return</span> <span class="hljs-string">'Three'</span>;
              }
            },
          ),
        ],
      ),
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 2'</span>,
        icon: Icons.add_circle,
        children: [
          PlutoMenuItem(
            title: <span class="hljs-string">'Menu 2-1'</span>,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 2-1 tap'</span>),
          ),
        ],
      ),
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 3'</span>,
        icon: Icons.apps_outlined,
        onTap: () =&gt; message(context, <span class="hljs-string">'Menu 3 tap'</span>),
      ),
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 4'</span>,
        onTap: () =&gt; message(context, <span class="hljs-string">'Menu 4 tap'</span>),
      ),
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 5'</span>,
        onTap: () =&gt; message(context, <span class="hljs-string">'Menu 5 tap'</span>),
      ),
      PlutoMenuItem(
        title: <span class="hljs-string">'Menu 6'</span>,
        children: [
          PlutoMenuItem(
            title: <span class="hljs-string">'Menu 6-1'</span>,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-1 tap'</span>),
            children: [
              PlutoMenuItem(
                title: <span class="hljs-string">'Menu 6-1-1'</span>,
                onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-1-1 tap'</span>),
                children: [
                  PlutoMenuItem(
                    title: <span class="hljs-string">'Menu 6-1-1-1'</span>,
                    onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-1-1-1 tap'</span>),
                  ),
                  PlutoMenuItem(
                    title: <span class="hljs-string">'Menu 6-1-1-2'</span>,
                    onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-1-1-2 tap'</span>),
                  ),
                ],
              ),
              PlutoMenuItem(
                title: <span class="hljs-string">'Menu 6-1-2'</span>,
                onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-1-2 tap'</span>),
              ),
            ],
          ),
          PlutoMenuItem(
            title: <span class="hljs-string">'Menu 6-2'</span>,
            onTap: () =&gt; message(context, <span class="hljs-string">'Menu 6-2 tap'</span>),
          ),
        ],
      ),
    ];
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Column(
      children: [
        <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">30</span>),
        PlutoMenuBar(
          menus: getMenus(context),
        ),
        <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">30</span>),
        PlutoMenuBar(
          backgroundColor: Colors.deepOrange,
          itemStyle: PlutoMenuItemStyle(
            activatedColor: Colors.white,
            indicatorColor: Colors.deepOrange,
            textStyle: <span class="hljs-keyword">const</span> TextStyle(color: Colors.white),
            moreIconColor: Colors.white,
          ),
          menus: getMenus(context),
        ),
        <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">30</span>),
        PlutoMenuBar(
          backgroundColor: Colors.orange,
          height: <span class="hljs-number">55</span>,
          itemStyle: PlutoMenuItemStyle(
            activatedColor: Colors.white,
            indicatorColor: Colors.orange,
            textStyle: <span class="hljs-keyword">const</span> TextStyle(color: Colors.white, fontSize: <span class="hljs-number">18</span>),
            moreIconColor: Colors.white,
          ),
          menus: getMenus(context),
        ),
        <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">30</span>),
        PlutoMenuBar(
          backgroundColor: Colors.black,
          itemStyle: PlutoMenuItemStyle(
            iconColor: Colors.white,
            iconSize: <span class="hljs-number">26</span>,
            moreIconColor: Colors.white,
            activatedColor: Colors.white,
            unselectedColor: Colors.white70,
            indicatorColor: Colors.black,
            textStyle: <span class="hljs-keyword">const</span> TextStyle(color: Colors.white, fontSize: <span class="hljs-number">20</span>),
          ),
          height: <span class="hljs-number">65</span>,
          menus: getMenus(context),
        ),
      ],
    );
  }
}

<span class="hljs-keyword">enum</span> _RadioItems {
  one,
  two,
  three,
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Rows and Cells]]></title><description><![CDATA[Default properties of rows
Rows should be passed as a List<PlutoRow> type to the rows property when creating a PlutoGrid.
It can also be passed as an empty list.
final List<PlutoRow> rows = [
  PlutoRow(
    cells: {
      'id': PlutoCell(value: 'use...]]></description><link>https://pluto.weblaze.dev/rows-and-cells</link><guid isPermaLink="true">https://pluto.weblaze.dev/rows-and-cells</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[datatable]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Tue, 12 Jul 2022 03:43:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657595859410/mBAA8PH81.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-default-properties-of-rows">Default properties of rows</h3>
<p>Rows should be passed as a <code>List&lt;PlutoRow&gt;</code> type to the <code>rows</code> property when creating a <code>PlutoGrid</code>.
It can also be passed as an empty list.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows = [
  PlutoRow(
    cells: {
      <span class="hljs-string">'id'</span>: PlutoCell(value: <span class="hljs-string">'user1'</span>),
      <span class="hljs-string">'name'</span>: PlutoCell(value: <span class="hljs-string">'Mike'</span>),
      <span class="hljs-string">'age'</span>: PlutoCell(value: <span class="hljs-number">20</span>),
      <span class="hljs-string">'role'</span>: PlutoCell(value: <span class="hljs-string">'Programmer'</span>),
      <span class="hljs-string">'joined'</span>: PlutoCell(value: <span class="hljs-string">'2021-01-01'</span>),
      <span class="hljs-string">'working_time'</span>: PlutoCell(value: <span class="hljs-string">'09:00'</span>),
    },
  ),
];

Widget build(BuildContext context) {
  <span class="hljs-keyword">return</span> Scaffold(
    body: Container(
      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
      child: PlutoGrid(
        columns: columns,
        rows: rows,
      ),
    ),
  );
}
</code></pre>
<p><code>cells</code> should be set to <code>Map&lt;String, PlutoCell&gt;</code> type with the <code>PlutoColumn.field</code> value set when creating <code>columns</code> as a key.</p>
<pre><code class="lang-dart">PlutoRow(checked: <span class="hljs-keyword">true</span>);
</code></pre>
<p>The <code>checked</code> property is whether the checkbox is checked when <code>PlutoColumn.enableRowChecked</code> is set to <code>true</code>.
This value can be set at startup, and the value can be changed during runtime during grid operation.</p>
<pre><code class="lang-dart">PlutoRow(sortIdx: <span class="hljs-number">0</span>);
</code></pre>
<p>The <code>sortIdx</code> value indicates the first sort state of the row. When creating a grid, this value is automatically set according to the list order. If this value is set, this value is not automatically set when the grid is created.
This value is used to return to the original sort state when the sort state of rows is changed.</p>
<h3 id="heading-default-properties-of-cells">Default properties of cells</h3>
<p>Cells should be set to match the value of <code>PlutoColumn.field</code> when creating <code>PlutoRow</code>.<br /><code>PlutoCell.value</code> must be set to a value that matches <code>PlutoColumn.type</code>. (text, number, date, etc.)<br />If <code>PlutoColumn.type</code> is a type with a format such as number or date, the value of <code>PlutoCell.value</code> can be changed according to the format. (if <code>PlutoColumn.applyFormatterInEditing</code> value is set to <code>true</code>)  </p>
<pre><code class="lang-dart">PlutoRow(
  cells: {
    <span class="hljs-string">'id'</span>: PlutoCell(value: <span class="hljs-string">'user1'</span>),
    <span class="hljs-string">'name'</span>: PlutoCell(value: <span class="hljs-string">'Mike'</span>),
    <span class="hljs-string">'age'</span>: PlutoCell(value: <span class="hljs-number">20</span>),
    <span class="hljs-string">'role'</span>: PlutoCell(value: <span class="hljs-string">'Programmer'</span>),
    <span class="hljs-string">'joined'</span>: PlutoCell(value: <span class="hljs-string">'2021-01-01'</span>),
    <span class="hljs-string">'working_time'</span>: PlutoCell(value: <span class="hljs-string">'09:00'</span>),
  },
),
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Columns]]></title><description><![CDATA[Default properties of columns
Columns must be passed to the columns property, which is a List<PlutoColumn> type when creating a PlutoGrid.
final List<PlutoColumn> columns = [
  PlutoColumn(
    title: 'Id',
    field: 'id',
    type: PlutoColumnType....]]></description><link>https://pluto.weblaze.dev/columns</link><guid isPermaLink="true">https://pluto.weblaze.dev/columns</guid><category><![CDATA[PlutoGrid]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[column]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 11 Jul 2022 11:24:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657538859336/mwn15h_1b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-default-properties-of-columns">Default properties of columns</h3>
<p>Columns must be passed to the <code>columns</code> property, which is a <code>List&lt;PlutoColumn&gt;</code> type when creating a <code>PlutoGrid</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns = [
  PlutoColumn(
    title: <span class="hljs-string">'Id'</span>,
    field: <span class="hljs-string">'id'</span>,
    type: PlutoColumnType.text(),
  ),
  PlutoColumn(
    title: <span class="hljs-string">'Name'</span>,
    field: <span class="hljs-string">'name'</span>,
    type: PlutoColumnType.number(),
  ),
  PlutoColumn(
    title: <span class="hljs-string">'Age'</span>,
    field: <span class="hljs-string">'age'</span>,
    type: PlutoColumnType.date(),
  ),
];

Widget build(BuildContext context) {
  <span class="hljs-keyword">return</span> Scaffold(
    body: Container(
      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
      child: PlutoGrid(
        columns: columns,
        rows: rows,
      ),
    ),
  );
}
</code></pre>
<p><code>title</code> is the title of the column displayed on the screen.<br /><code>field</code> is the only value that identifies the column inside <code>PlutoGrid</code>.<br /><code>type</code> determines the type of the column.  </p>
<h3 id="heading-type-of-column">Type of column</h3>
<p>You can pass <code>defaultValue</code> to the constructor for each column type.<br /><code>defaultValue</code> is used as the default value when a new row is added.  </p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Id'</span>,
  field: <span class="hljs-string">'id'</span>,
  type: PlutoColumnType.text(defaultValue: <span class="hljs-string">'default value'</span>),
),
</code></pre>
<h4 id="heading-text-column">Text column</h4>
<p>This is a column where text can be entered or edited.</p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Id'</span>,
  field: <span class="hljs-string">'id'</span>,
  type: PlutoColumnType.text(),
),
</code></pre>
<h4 id="heading-number-column">Number column</h4>
<p>This is a column where numbers can be entered or edited.</p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Age'</span>,
  field: <span class="hljs-string">'age'</span>,
  type: PlutoColumnType.number(
    negative: <span class="hljs-keyword">false</span>,
    format: <span class="hljs-string">'#,###'</span>,
    applyFormatOnInit: <span class="hljs-keyword">true</span>,
    allowFirstDot: <span class="hljs-keyword">false</span>,
  ),
),
</code></pre>
<p><code>negative</code> determines whether negative numbers are allowed. Default is <code>true</code>.</p>
<p><code>format</code> determines the format in which numbers are displayed. The default is <code>#,###</code>. (Follows NumberFormat from Intl package.)</p>
<p><code>applyFormatOnInit</code> determines whether values are initialized according to <code>format</code> when a row is first added. If the first set value is 1.123 and format does not accept decimal points, the value is initialized to 1. Default is <code>true</code> .</p>
<p><code>allowFirstDot</code> allows a dot at the beginning if it allows negative numbers. This option is required on devices where the <code>.-</code> symbol works as a single button (some mobile terminals). The default is <code>false</code> .</p>
<h4 id="heading-select-column">Select column</h4>
<p>This is a column where you can select one of several set items.</p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Role'</span>,
  field: <span class="hljs-string">'role'</span>,
  type: PlutoColumnType.select(
    &lt;<span class="hljs-built_in">String</span>&gt;[
      <span class="hljs-string">'Programmer'</span>,
      <span class="hljs-string">'Designer'</span>,
      <span class="hljs-string">'Owner'</span>,
    ],
    enableColumnFilter: <span class="hljs-keyword">false</span>,
  ),
),
</code></pre>
<p>The user can select one item from the range of items in the <code>List</code> passed when creating <code>select</code>.
A built-in popup opens in the grid, allowing you to select it with your keyboard or mouse.</p>
<p><code>enableColumnFilter</code> is whether to enable filtering on the built-in popup. The default is <code>false</code> . If enabled, you can use the filtering function in the pop-up.</p>
<h4 id="heading-date-column">Date column</h4>
<p>This is a column where you can enter or edit the date.</p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Joined'</span>,
  field: <span class="hljs-string">'joined'</span>,
  type: PlutoColumnType.date(
    startDate: <span class="hljs-built_in">DateTime</span>(<span class="hljs-number">2022</span>, <span class="hljs-number">01</span>, <span class="hljs-number">01</span>),
    endDate: <span class="hljs-built_in">DateTime</span>(<span class="hljs-number">2022</span>, <span class="hljs-number">01</span>, <span class="hljs-number">31</span>),
    format: <span class="hljs-string">'yyyy-MM-dd'</span>,
    headerFormat: <span class="hljs-string">'yyyy-MM'</span>,
    applyFormatOnInit: <span class="hljs-keyword">true</span>,
  ),
),
</code></pre>
<p><code>startDate</code> can limit the optional starting range of dates. If not set, there is no limit to the starting range when selecting a date.</p>
<p><code>endDate</code> can constrain the selection end range of dates. If not set, there is no limit to the ending range in the date selection.</p>
<p><code>format</code> determines the format of the date. (Follows DateFormat in Intl package.)</p>
<p><code>headerFormat</code> determines the format when displaying the current year and month at the top in the date picker popup.</p>
<p><code>applyFormatOnInit</code> is whether to change the value to match <code>format</code> when a row is first added. If <code>format</code> is 'yyyy-MM-ddd` and the value is '2022-01-02 00:00:00', the value is changed to '2022-01-02'.</p>
<h4 id="heading-time-column">Time column</h4>
<p>This is a column where time can be entered or edited.
A popup is called to select the hour and minute in the form of <code>00:00</code>.</p>
<pre><code class="lang-dart">PlutoColumn(
  title: <span class="hljs-string">'Working time'</span>,
  field: <span class="hljs-string">'working_time'</span>,
  type: PlutoColumnType.time(),
),
</code></pre>
<h3 id="heading-additional-column-properties">Additional column properties</h3>
<p>Additional properties such as limiting the edit status of a column or adding an icon to the column title.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the column to non-modifiable read mode.</span></span>
PlutoColumn(readOnly: <span class="hljs-keyword">true</span>);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the column width and minimum width.</span></span>
PlutoColumn(width: <span class="hljs-number">200.0</span>, minWidth: <span class="hljs-number">80.0</span>);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the padding of the column title and filter area.</span></span>
PlutoColumn(
   titlePadding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
   filterPadding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">You can customize the column title.</span></span>
PlutoColumn(
 titleSpan: <span class="hljs-keyword">const</span> TextSpan(
   children: [
     WidgetSpan(
       child: Text(
         <span class="hljs-string">'* '</span>,
         style: TextStyle(color: Colors.red),
       ),
     ),
     TextSpan(text: <span class="hljs-string">'column title'</span>),
   ],
 ),
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the padding of the cells of the corresponding column.</span></span>
PlutoColumn(
   cellPadding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the column title and text alignment of the cells of the column.</span></span>
PlutoColumn(
   textAlign: PlutoColumnTextAlign.start,
   titleTextAlign: PlutoColumnTextAlign.end,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Freeze the column at the beginning or end.</span></span>
<span class="hljs-comment">/// <span class="markdown">If the total width of the non-frozen column is 200 or less, </span></span>
<span class="hljs-comment">/// <span class="markdown">it is processed to be unfreeze even if the frozen column is set.</span></span>
PlutoColumn(
   frozen: PlutoColumnFrozen.start,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Sort the cells in the column.</span></span>
PlutoColumn(
   sort: PlutoColumnSort.ascending,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Customize the format of the cells in the column.</span></span>
<span class="hljs-comment">/// <span class="markdown">The example changes the output </span></span>
<span class="hljs-comment">/// <span class="markdown">to say (1) Allowed in the cell if the actual value is the number '1'.</span></span>
PlutoColumn(
  title: <span class="hljs-string">'Permission readonly'</span>,
  field: <span class="hljs-string">'permission_readonly'</span>,
  readOnly: <span class="hljs-keyword">true</span>,
  type: PlutoColumnType.number(),
  <span class="hljs-comment">/// <span class="markdown">Apply the formatter in the editing state.</span></span>
  <span class="hljs-comment">/// <span class="markdown">However, it is applied only when the cell is readonly</span></span>
  <span class="hljs-comment">/// <span class="markdown">or the text cannot be directly modified, </span></span>
  <span class="hljs-comment">/// <span class="markdown">such as in the form of select popup.</span></span>
  applyFormatterInEditing: <span class="hljs-keyword">true</span>,
  formatter: (<span class="hljs-built_in">dynamic</span> value) {
    <span class="hljs-keyword">if</span> (value.toString() == <span class="hljs-string">'1'</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'(1) Allowed'</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'(0) Disallowed'</span>;
    }
  },
),
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Set the background color of the column.</span></span>
PlutoColumn(
  backgroundColor: Colors.white,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">You can customize the cells of the column.</span></span>
<span class="hljs-comment">/// <span class="markdown">The following is an example of dynamically changing </span></span>
<span class="hljs-comment">/// <span class="markdown">the text color according to the selected item value </span></span>
<span class="hljs-comment">/// <span class="markdown">in the select type column.</span></span>
PlutoColumn(
  title: <span class="hljs-string">'column2'</span>,
  field: <span class="hljs-string">'column2'</span>,
  type: PlutoColumnType.select(&lt;<span class="hljs-built_in">String</span>&gt;[<span class="hljs-string">'red'</span>, <span class="hljs-string">'blue'</span>, <span class="hljs-string">'green'</span>]),
  renderer: (rendererContext) {
    Color textColor = Colors.black;

    <span class="hljs-keyword">if</span> (rendererContext.cell.value == <span class="hljs-string">'red'</span>) {
      textColor = Colors.red;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rendererContext.cell.value == <span class="hljs-string">'blue'</span>) {
      textColor = Colors.blue;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rendererContext.cell.value == <span class="hljs-string">'green'</span>) {
      textColor = Colors.green;
    }

    <span class="hljs-keyword">return</span> Text(
      rendererContext.cell.value.toString(),
      style: TextStyle(
        color: textColor,
        fontWeight: FontWeight.bold,
      ),
    );
  },
),
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Decide whether the position can be moved by dragging the column.</span></span>
<span class="hljs-comment">/// <span class="markdown">If set to <span class="hljs-code">`true`</span>, you can move the column </span></span>
<span class="hljs-comment">/// <span class="markdown">to the position of another column by dragging it.</span></span>
PlutoColumn(
  enableColumnDrag: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Displays an icon that can drag a row to the cells of the column.</span></span>
<span class="hljs-comment">/// <span class="markdown">You can move the row position by dragging this icon.</span></span>
PlutoColumn(
  enableRowDrag: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Whether to display a check box in the column and its cells.</span></span>
PlutoColumn(
  enableRowChecked: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Whether the column can be sorted.</span></span>
<span class="hljs-comment">/// <span class="markdown">If set to <span class="hljs-code">`true`</span>, you can sort by tapping the column heading.</span></span>
PlutoColumn(
  enableSorting: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableContextMenu`</span> is <span class="hljs-code">`true`</span>, </span></span>
<span class="hljs-comment">/// <span class="markdown">an icon that can invoke the context menu is displayed </span></span>
<span class="hljs-comment">/// <span class="markdown">at the end of the column title.</span></span>
<span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableDropToResize`</span> is <span class="hljs-code">`true`</span>, </span></span>
<span class="hljs-comment">/// <span class="markdown">you can change the column width by dragging the menu icon.</span></span>
<span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableContextMenu`</span> is <span class="hljs-code">`true`</span>, </span></span>
<span class="hljs-comment">/// <span class="markdown">set <span class="hljs-code">`enableFilterMenuItem`</span> to <span class="hljs-code">`false`</span> to hide filter related menus.</span></span>
<span class="hljs-comment">/// <span class="markdown">When <span class="hljs-code">`enableContextMenu`</span> is <span class="hljs-code">`true`</span>, if <span class="hljs-code">`enableHideColumnMenuItem`</span> </span></span>
<span class="hljs-comment">/// <span class="markdown">is set to <span class="hljs-code">`false`</span>, the menu for hiding columns is not displayed.</span></span>
<span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableContextMenu`</span> is <span class="hljs-code">`true`</span>, set <span class="hljs-code">`enableSetColumnsMenuItem`</span> to <span class="hljs-code">`false`</span> </span></span>
<span class="hljs-comment">/// <span class="markdown">to hide the column setting menu.</span></span>
PlutoColumn(
  enableContextMenu: <span class="hljs-keyword">true</span>,
  enableDropToResize: <span class="hljs-keyword">true</span>,
  enableFilterMenuItem: <span class="hljs-keyword">true</span>,
  enableHideColumnMenuItem: <span class="hljs-keyword">true</span>,
  enableSetColumnsMenuItem: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableAutoEditing`</span> is <span class="hljs-code">`true`</span>, the cell is automatically changed </span></span>
<span class="hljs-comment">/// <span class="markdown">to edit state when it receives focus.</span></span>
<span class="hljs-comment">/// <span class="markdown">If <span class="hljs-code">`enableEditingMode`</span> is <span class="hljs-code">`true`</span>, tap a cell or press Enter </span></span>
<span class="hljs-comment">/// <span class="markdown">to change to editing state.</span></span>
PlutoColumn(
  enableAutoEditing: <span class="hljs-keyword">true</span>,
  enableEditingMode: <span class="hljs-keyword">true</span>,
);
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">You can hide the column.</span></span>
PlutoColumn(
  <span class="hljs-keyword">hide</span>: <span class="hljs-keyword">true</span>,
);
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Getting Started]]></title><description><![CDATA[Search for pluto_grid in pub.dev and add the package to your project.pub.dev to install pluto_grid
And add PlutoGrid to your screen by referring to the example code below.   
import 'package:flutter/material.dart';
import 'package:pluto_grid/pluto_gr...]]></description><link>https://pluto.weblaze.dev/getting-started</link><guid isPermaLink="true">https://pluto.weblaze.dev/getting-started</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[datatable]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 11 Jul 2022 09:36:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657531271015/8Z4aEGr8t.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657531399928/Umug2Tntm.png" alt="화면 캡처 2022-07-11 182303.png" /></p>
<p>Search for pluto_grid in pub.dev and add the package to your project.<br /><a target="_blank" href="https://pub.dev/packages/pluto_grid/install">pub.dev to install pluto_grid</a></p>
<p>And add PlutoGrid to your screen by referring to the example code below.   </p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:pluto_grid/pluto_grid.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(<span class="hljs-keyword">const</span> MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'PlutoGrid Example'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: <span class="hljs-keyword">const</span> PlutoGridExamplePage(),
    );
  }
}

<span class="hljs-comment">/// <span class="markdown">PlutoGrid Example</span></span>
<span class="hljs-comment">//</span>
<span class="hljs-comment">/// <span class="markdown">For more examples, go to the demo web link on the github below.</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PlutoGridExamplePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> PlutoGridExamplePage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  State&lt;PlutoGridExamplePage&gt; createState() =&gt; _PlutoGridExamplePageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_PlutoGridExamplePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">PlutoGridExamplePage</span>&gt; </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoColumn&gt; columns = &lt;PlutoColumn&gt;[
    PlutoColumn(
      title: <span class="hljs-string">'Id'</span>,
      field: <span class="hljs-string">'id'</span>,
      type: PlutoColumnType.text(),
    ),
    PlutoColumn(
      title: <span class="hljs-string">'Name'</span>,
      field: <span class="hljs-string">'name'</span>,
      type: PlutoColumnType.text(),
    ),
    PlutoColumn(
      title: <span class="hljs-string">'Age'</span>,
      field: <span class="hljs-string">'age'</span>,
      type: PlutoColumnType.number(),
    ),
    PlutoColumn(
      title: <span class="hljs-string">'Role'</span>,
      field: <span class="hljs-string">'role'</span>,
      type: PlutoColumnType.select(&lt;<span class="hljs-built_in">String</span>&gt;[
        <span class="hljs-string">'Programmer'</span>,
        <span class="hljs-string">'Designer'</span>,
        <span class="hljs-string">'Owner'</span>,
      ]),
    ),
    PlutoColumn(
      title: <span class="hljs-string">'Joined'</span>,
      field: <span class="hljs-string">'joined'</span>,
      type: PlutoColumnType.date(),
    ),
    PlutoColumn(
      title: <span class="hljs-string">'Working time'</span>,
      field: <span class="hljs-string">'working_time'</span>,
      type: PlutoColumnType.time(),
    ),
  ];

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoRow&gt; rows = [
    PlutoRow(
      cells: {
        <span class="hljs-string">'id'</span>: PlutoCell(value: <span class="hljs-string">'user1'</span>),
        <span class="hljs-string">'name'</span>: PlutoCell(value: <span class="hljs-string">'Mike'</span>),
        <span class="hljs-string">'age'</span>: PlutoCell(value: <span class="hljs-number">20</span>),
        <span class="hljs-string">'role'</span>: PlutoCell(value: <span class="hljs-string">'Programmer'</span>),
        <span class="hljs-string">'joined'</span>: PlutoCell(value: <span class="hljs-string">'2021-01-01'</span>),
        <span class="hljs-string">'working_time'</span>: PlutoCell(value: <span class="hljs-string">'09:00'</span>),
      },
    ),
    PlutoRow(
      cells: {
        <span class="hljs-string">'id'</span>: PlutoCell(value: <span class="hljs-string">'user2'</span>),
        <span class="hljs-string">'name'</span>: PlutoCell(value: <span class="hljs-string">'Jack'</span>),
        <span class="hljs-string">'age'</span>: PlutoCell(value: <span class="hljs-number">25</span>),
        <span class="hljs-string">'role'</span>: PlutoCell(value: <span class="hljs-string">'Designer'</span>),
        <span class="hljs-string">'joined'</span>: PlutoCell(value: <span class="hljs-string">'2021-02-01'</span>),
        <span class="hljs-string">'working_time'</span>: PlutoCell(value: <span class="hljs-string">'10:00'</span>),
      },
    ),
    PlutoRow(
      cells: {
        <span class="hljs-string">'id'</span>: PlutoCell(value: <span class="hljs-string">'user3'</span>),
        <span class="hljs-string">'name'</span>: PlutoCell(value: <span class="hljs-string">'Suzi'</span>),
        <span class="hljs-string">'age'</span>: PlutoCell(value: <span class="hljs-number">40</span>),
        <span class="hljs-string">'role'</span>: PlutoCell(value: <span class="hljs-string">'Owner'</span>),
        <span class="hljs-string">'joined'</span>: PlutoCell(value: <span class="hljs-string">'2021-03-01'</span>),
        <span class="hljs-string">'working_time'</span>: PlutoCell(value: <span class="hljs-string">'11:00'</span>),
      },
    ),
  ];

  <span class="hljs-comment">/// <span class="markdown">columnGroups that can group columns can be omitted.</span></span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;PlutoColumnGroup&gt; columnGroups = [
    PlutoColumnGroup(title: <span class="hljs-string">'Id'</span>, fields: [<span class="hljs-string">'id'</span>], expandedColumn: <span class="hljs-keyword">true</span>),
    PlutoColumnGroup(title: <span class="hljs-string">'User information'</span>, fields: [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>]),
    PlutoColumnGroup(title: <span class="hljs-string">'Status'</span>, children: [
      PlutoColumnGroup(title: <span class="hljs-string">'A'</span>, fields: [<span class="hljs-string">'role'</span>], expandedColumn: <span class="hljs-keyword">true</span>),
      PlutoColumnGroup(title: <span class="hljs-string">'Etc.'</span>, fields: [<span class="hljs-string">'joined'</span>, <span class="hljs-string">'working_time'</span>]),
    ]),
  ];

  <span class="hljs-comment">/// <span class="markdown">[PlutoGridStateManager] has many methods and properties to dynamically manipulate the grid.</span></span>
  <span class="hljs-comment">/// <span class="markdown">You can manipulate the grid dynamically at runtime by passing this through the [onLoaded] callback.</span></span>
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> PlutoGridStateManager stateManager;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">15</span>),
        child: PlutoGrid(
          columns: columns,
          rows: rows,
          columnGroups: columnGroups,
          onLoaded: (PlutoGridOnLoadedEvent event) {
            stateManager = event.stateManager;
          },
          onChanged: (PlutoGridOnChangedEvent event) {
            <span class="hljs-built_in">print</span>(event);
          },
          configuration: <span class="hljs-keyword">const</span> PlutoGridConfiguration(),
        ),
      ),
    );
  }
}
</code></pre>
<p>Columns and rows can start with an empty list.<br />Column groups can be excluded if not needed.<br /><code>PlutoColumn.field</code> cannot be duplicated.<br />The keys in <code>PlutoRow.cells</code> must be defined to match <code>PlutoColumn.field</code>.   </p>
]]></content:encoded></item><item><title><![CDATA[Introduction to PlutoGrid.]]></title><description><![CDATA[PlutoGrid is a Flutter package and is a data grid used on all platforms supported by Flutter.It is also open source and the source code is open to Github.
https://www.youtube.com/watch?v=7lZGowuhd94
Features

Freeze the column left and right
Drag the...]]></description><link>https://pluto.weblaze.dev/introduction-to-plutogrid</link><guid isPermaLink="true">https://pluto.weblaze.dev/introduction-to-plutogrid</guid><category><![CDATA[Flutter]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datatable]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 11 Jul 2022 08:48:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657530007731/UhwO90oHg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PlutoGrid is a Flutter package and is a data grid used on all platforms supported by Flutter.<br />It is also open source and the source code is open to Github.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=7lZGowuhd94">https://www.youtube.com/watch?v=7lZGowuhd94</a></div>
<h3 id="heading-features">Features</h3>
<ul>
<li>Freeze the column left and right</li>
<li>Drag the column to move it</li>
<li>Column grouping</li>
<li>Column filtering and sorting</li>
<li>Automatically adjust column width</li>
<li>Move by dragging a row</li>
<li>Row pagination</li>
<li>Customizable columns and cells</li>
<li>Interlock keyboard movement between two grids</li>
<li>Call the grid in popup form</li>
<li>Select mode for select type data</li>
<li>Keyboard shortcut support</li>
<li>Support for layout and keyboard actions for RTL languages</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657530116547/x0kCYeqHh.png" alt="datepicker.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657530263042/f7XcPQt8c.png" alt="selection_cells.png" /></p>
<h3 id="heading-links">Links</h3>
<ul>
<li><a target="_blank" href="https://weblaze.dev/pluto_grid/build/web/#/">Live demo</a></li>
<li><a target="_blank" href="https://pub.dev/packages/pluto_grid">Pub.dev</a></li>
<li><a target="_blank" href="https://github.com/bosskmk/pluto_grid">Github</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[PlutoMenuBar Live Demo]]></title><description><![CDATA[PlutoMenuBar Live Demo
A link to a live demo where you can see PlutoMenuBar in action on the web.]]></description><link>https://pluto.weblaze.dev/plutomenubar-live-demo</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutomenubar-live-demo</guid><category><![CDATA[Flutter]]></category><category><![CDATA[LiveDemo]]></category><category><![CDATA[MenuBar]]></category><category><![CDATA[PlutoMenuBar]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 11 Jul 2022 08:12:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657530713500/uIIHiS3RH.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://weblaze.dev/pluto_menu_bar/build/web/#/">PlutoMenuBar Live Demo</a></p>
<p>A link to a live demo where you can see PlutoMenuBar in action on the web.</p>
]]></content:encoded></item><item><title><![CDATA[PlutoGrid Live Demo]]></title><description><![CDATA[PlutoGrid Live Demo
A link to a live demo where you can see PlutoGrid in action on the web.]]></description><link>https://pluto.weblaze.dev/plutogrid-live-demo</link><guid isPermaLink="true">https://pluto.weblaze.dev/plutogrid-live-demo</guid><category><![CDATA[Flutter]]></category><category><![CDATA[PlutoGrid]]></category><category><![CDATA[datagrid]]></category><category><![CDATA[LiveDemo]]></category><dc:creator><![CDATA[manki kim]]></dc:creator><pubDate>Mon, 11 Jul 2022 08:09:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657530592006/WYzIP93DX.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://weblaze.dev/pluto_grid/build/web/#/">PlutoGrid Live Demo</a></p>
<p>A link to a live demo where you can see PlutoGrid in action on the web.</p>
]]></content:encoded></item></channel></rss>