Skip to content

A powerful mind map widget for Flutter, which supports multiple layouts, smart camera focus, interactive expand/collapse, custom node shapes, dynamic sizing, smooth animations, pan & zoom navigation, and rich styling options.

License

Notifications You must be signed in to change notification settings

devpark435/reactive_mind_map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

45 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Reactive Mind Map / ๋ฐ˜์‘ํ˜• ๋งˆ์ธ๋“œ๋งต

pub package License: MIT

A highly customizable and interactive mind map package for Flutter with multiple layouts, dynamic sizing, and rich styling options.

Flutter์šฉ ๋‹ค์ค‘ ๋ ˆ์ด์•„์›ƒ, ๋™์  ํฌ๊ธฐ ์กฐ์ ˆ, ๋‹ค์–‘ํ•œ ์Šคํƒ€์ผ๋ง ์˜ต์…˜์„ ์ œ๊ณตํ•˜๋Š” ๊ณ ๋„๋กœ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅํ•œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋งˆ์ธ๋“œ๋งต ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.

Screenshots / ์Šคํฌ๋ฆฐ์ƒท

Reactive Mind Map Demo

Multiple layouts and customization options / ๋‹ค์–‘ํ•œ ๋ ˆ์ด์•„์›ƒ๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ์˜ต์…˜

Demo / ๋ฐ๋ชจ

Interactive Mind Map Animation

Interactive expand/collapse and smooth animations / ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ํ™•์žฅ/์ถ•์†Œ ๋ฐ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜

Features / ํŠน์ง•

๐ŸŽจ ์™„์ „ํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• / Complete Customization

  • ๋…ธ๋“œ ๋ชจ์–‘ ์„ ํƒ (๋‘ฅ๊ทผ ์‚ฌ๊ฐํ˜•, ์›ํ˜•, ๋‹ค์ด์•„๋ชฌ๋“œ, ์œก๊ฐํ˜• ๋“ฑ) / Node shapes (rounded rectangle, circle, diamond, hexagon, etc.)
  • ์ƒ‰์ƒ, ํ…์ŠคํŠธ ์Šคํƒ€์ผ, ๊ทธ๋ฆผ์ž ํšจ๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• / Colors, text styles, shadow effects customization
  • ๋™์  ๋…ธ๋“œ ํฌ๊ธฐ ์กฐ์ ˆ / Dynamic node sizing
  • ์—ฐ๊ฒฐ์„  ์Šคํƒ€์ผ๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ • / Connection line styles and animation settings

๐ŸŽฏ ๋‹ค์–‘ํ•œ ๋ ˆ์ด์•„์›ƒ / Multiple Layouts

  • ์˜ค๋ฅธ์ชฝ/์™ผ์ชฝ/์œ„/์•„๋ž˜ ๋ฐฉํ–ฅ ๋ ˆ์ด์•„์›ƒ / Right/Left/Top/Bottom direction layouts
  • ์›ํ˜•(Radial) ๋ ˆ์ด์•„์›ƒ / Radial layout
  • ์ขŒ์šฐ/์ƒํ•˜ ๋ถ„ํ•  ๋ ˆ์ด์•„์›ƒ / Horizontal/Vertical split layouts

โšก ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ / Smooth Animations

  • ๋…ธ๋“œ ํ™•์žฅ/์ถ•์†Œ ์• ๋‹ˆ๋ฉ”์ด์…˜ / Node expand/collapse animations
  • ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณก์„ ๊ณผ ์ง€์†์‹œ๊ฐ„ / Customizable animation curves and duration
  • ํ•˜๋“œ์›จ์–ด ๊ฐ€์† ํŠธ๋žœ์ง€์…˜ / Hardware-accelerated transitions

๐Ÿ–ฑ๏ธ ํ’๋ถ€ํ•œ ์ธํ„ฐ๋ž™์…˜ / Rich Interactions

  • ํƒญ, ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๊ธฐ, ๋”๋ธ” ํƒญ ์ด๋ฒคํŠธ / Tap, long press, double tap events
  • ํ™•๋Œ€/์ถ•์†Œ, ํŒฌ ๊ธฐ๋Šฅ / Pan & zoom functionality
  • ๋…ธ๋“œ ํ™•์žฅ/์ถ•์†Œ ์ƒํƒœ ์ถ”์  / Node expand/collapse state tracking

๐ŸŽฏ ์Šค๋งˆํŠธ ์นด๋ฉ”๋ผ ํฌ์ปค์Šค / Smart Camera Focus ๐Ÿ†•

  • ์ž๋™ ์ „์ฒด๋ณด๊ธฐ๋กœ ์ž‘์€ ์œ„์ ฏ์—์„œ๋„ ์ตœ์  ํ‘œ์‹œ / Auto-fit for optimal display in small widgets
  • ํŠน์ • ๋…ธ๋“œ ๊ฐ•์กฐ ๋ฐ ๊ฐ€์ด๋“œ ํˆฌ์–ด ์ง€์› / Specific node highlighting and guided tours
  • ๋ถ€๋“œ๋Ÿฌ์šด ํฌ์ปค์Šค ์ด๋™ ์• ๋‹ˆ๋ฉ”์ด์…˜ / Smooth focus transition animations
  • 5๊ฐ€์ง€ ํฌ์ปค์Šค ๋ชจ๋“œ (๋ฃจํŠธ, ์ค‘์•™, ์ „์ฒด, ๋ฆฌํ”„, ์ปค์Šคํ…€) / 5 focus modes (root, center, fitAll, leaf, custom)

Event Handling / ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

MindMapWidget(
  data: myData,
  onNodeTap: (node) => print('Node tapped: ${node.title}'),
  onNodeLongPress: (node) => _showNodeOptions(node),
  onNodeExpandChanged: (node, isExpanded) => 
    print('${node.title} ${isExpanded ? 'expanded' : 'collapsed'}'),
);

Installation / ์„ค์น˜

Add this to your package's pubspec.yaml file: pubspec.yaml ํŒŒ์ผ์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

dependencies:
  reactive_mind_map: ^1.0.3

Then run / ๊ทธ๋‹ค์Œ ์‹คํ–‰ํ•˜์„ธ์š”:

flutter pub get

Quick Start / ๋น ๋ฅธ ์‹œ์ž‘

import 'package:flutter/material.dart';
import 'package:reactive_mind_map/reactive_mind_map.dart';

class MyMindMap extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final mindMapData = MindMapData(
      id: 'root',
      title: 'My Project',
      children: [
        MindMapData(id: '1', title: 'Planning'),
        MindMapData(id: '2', title: 'Development'),
        MindMapData(id: '3', title: 'Testing'),
      ],
    );

    return Scaffold(
      body: MindMapWidget(
        data: mindMapData,
        style: MindMapStyle(
          layout: MindMapLayout.right,
          nodeShape: NodeShape.roundedRectangle,
        ),
        cameraFocus: CameraFocus.fitAll,
        focusAnimation: Duration(milliseconds: 500),
        onNodeTap: (node) => print('Tapped: ${node.title}'),
      ),
    );
  }
}

์ค‘์š” ์‚ฌ์šฉ๋ฒ• ์ฃผ์˜์‚ฌํ•ญ / Important Usage Notes

โš ๏ธ ํ™”๋ฉด ํฌ๊ธฐ ์ตœ์ ํ™” / Screen Size Optimization

  • MindMapWidget์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™”๋ฉด ํฌ๊ธฐ์— ๋งž๊ฒŒ ์ž๋™ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค
  • Expanded ์œ„์ ฏ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • ํŒฌ/์คŒ ๊ธฐ๋Šฅ์ด ๊ธฐ๋ณธ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์–ด ํฐ ๋งˆ์ธ๋“œ๋งต๋„ ์‰ฝ๊ฒŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
// โœ… ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ• - ํ™”๋ฉด์— ๋งž๊ฒŒ ์ž๋™ ์กฐ์ •
Widget build(BuildContext context) {
  return Scaffold(
    body: MindMapWidget(
      data: root.value,
      style: MindMapStyle(
        layout: MindMapLayout.right,
        nodeShape: NodeShape.roundedRectangle,
      ),
      onNodeTap: (node) => print('Tapped: ${node.title}'),
    ),
  );
}

// โœ… Expanded ์•ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: [
        SomeHeaderWidget(),
        Expanded(
          child: MindMapWidget(
            data: root.value,
            style: MindMapStyle(
              layout: MindMapLayout.right,
              nodeShape: NodeShape.roundedRectangle,
            ),
            onNodeTap: (node) => print('Tapped: ${node.title}'),
          ),
        ),
      ],
    ),
  );
}

Advanced Usage / ๊ณ ๊ธ‰ ์‚ฌ์šฉ๋ฒ•

Custom Node Builders / ์ปค์Šคํ…€ ๋…ธ๋“œ ๋นŒ๋” ๐Ÿ†•

You can create custom node designs using the nodeBuilder property in MindMapStyle: MindMapStyle์˜ nodeBuilder ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์Šคํ…€ ๋…ธ๋“œ ๋””์ž์ธ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

1. Style-Level Custom Node Builder / ์Šคํƒ€์ผ ๋ ˆ๋ฒจ ์ปค์Šคํ…€ ๋…ธ๋“œ ๋นŒ๋”

Use the nodeBuilder property in MindMapStyle: MindMapStyle์˜ nodeBuilder ์†์„ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

MindMapWidget(
  data: myData,
  style: MindMapStyle(
    layout: MindMapLayout.right,
    nodeShape: NodeShape.roundedRectangle,
    nodeBuilder: (node, isSelected, onTap, onLongPress, onDoubleTap) {
      return GestureDetector(
        onTap: onTap,
        onLongPress: onLongPress,
        onDoubleTap: onDoubleTap,
        child: Container(
          decoration: BoxDecoration(
            color: node.color,
            borderRadius: BorderRadius.circular(8),
            border: Border.all(
              color: isSelected ? Colors.yellow : Colors.transparent,
              width: 2,
            ),
          ),
          padding: EdgeInsets.all(12),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                node.title,
                style: TextStyle(
                  color: node.textColor ?? Colors.white,
                  fontWeight: FontWeight.bold,
                ),
                textAlign: TextAlign.center,
              ),
              if (node.description != null) ...[
                SizedBox(height: 4),
                Text(
                  node.description!,
                  style: TextStyle(
                    color: node.textColor?.withOpacity(0.8) ?? Colors.white70,
                    fontSize: 12,
                  ),
                  textAlign: TextAlign.center,
                ),
              ],
            ],
          ),
        ),
      );
    },
  ),
  onNodeTap: (node) => print('Tapped: ${node.title}'),
)

Camera Focus Control / ์นด๋ฉ”๋ผ ํฌ์ปค์Šค ์ œ์–ด

MindMapWidget(
  data: myData,
  cameraFocus: CameraFocus.fitAll,
  focusNodeId: 'specific_node_id',
  focusAnimation: Duration(milliseconds: 500),
  focusMargin: EdgeInsets.all(20),
)

Camera Focus Options / ์นด๋ฉ”๋ผ ํฌ์ปค์Šค ์˜ต์…˜

Focus Type / ํฌ์ปค์Šค ํƒ€์ž… When to Use / ์‚ฌ์šฉ ์‹œ๊ธฐ
CameraFocus.rootNode Default view / ๊ธฐ๋ณธ ๋ทฐ
CameraFocus.center Centered layouts / ์ค‘์•™ ์ •๋ ฌ ๋ ˆ์ด์•„์›ƒ
CameraFocus.fitAll Small widgets, overview / ์ž‘์€ ์œ„์ ฏ, ์ „์ฒด๋ณด๊ธฐ
CameraFocus.firstLeaf End-point focus / ๋์  ํฌ์ปค์Šค
CameraFocus.custom Specific node targeting / ํŠน์ • ๋…ธ๋“œ ํƒ€๊ฒŸํŒ…

Practical Examples / ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

1. Small Container Optimization / ์ž‘์€ ์ปจํ…Œ์ด๋„ˆ ์ตœ์ ํ™”

Container(
  height: 200,
  child: MindMapWidget(
    data: myData,
    cameraFocus: CameraFocus.fitAll,
    focusMargin: EdgeInsets.all(10),
    focusAnimation: Duration(milliseconds: 300),
  ),
)

2. Specific Node Highlighting / ํŠน์ • ๋…ธ๋“œ ๊ฐ•์กฐ

MindMapWidget(
  data: myData,
  cameraFocus: CameraFocus.custom,
  focusNodeId: 'important_milestone',
  focusAnimation: Duration(milliseconds: 800),
  initialScale: 1.2,
)

3. Guided Mind Map Tour / ๊ฐ€์ด๋“œ ๋งˆ์ธ๋“œ๋งต ํˆฌ์–ด

class GuidedMindMapTour extends StatefulWidget {
  @override
  State<GuidedMindMapTour> createState() => _GuidedMindMapTourState();
}

class _GuidedMindMapTourState extends State<GuidedMindMapTour> {
  int currentStep = 0;
  final List<String> tourSteps = ['intro', 'planning', 'development', 'testing'];

  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            ElevatedButton(
              onPressed: currentStep > 0 ? _previousStep : null,
              child: Text('์ด์ „'),
            ),
            Text('${currentStep + 1} / ${tourSteps.length}'),
            ElevatedButton(
              onPressed: currentStep < tourSteps.length - 1 ? _nextStep : null,
              child: Text('๋‹ค์Œ'),
            ),
          ],
        ),
        Expanded(
          child: MindMapWidget(
            data: myData,
            cameraFocus: CameraFocus.custom,
            focusNodeId: tourSteps[currentStep],
            focusAnimation: Duration(milliseconds: 600),
            focusMargin: EdgeInsets.all(50),
          ),
        ),
      ],
    );
  }

  void _nextStep() => setState(() => currentStep++);
  void _previousStep() => setState(() => currentStep--);
}

4. Dynamic Focus Based on Data / ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ฅธ ๋™์  ํฌ์ปค์Šค

Widget buildMindMap(MindMapData data) {
  final nodeCount = _countAllNodes(data);
  
  return MindMapWidget(
    data: data,
    cameraFocus: nodeCount > 10 ? CameraFocus.fitAll : CameraFocus.rootNode,
    focusAnimation: Duration(milliseconds: 400),
  );
}

Node Expand Camera Behavior / ๋…ธ๋“œ ํ™•์žฅ ์นด๋ฉ”๋ผ ๋™์ž‘ ๐Ÿ†•

Control how the camera behaves when users expand or collapse nodes: ์‚ฌ์šฉ์ž๊ฐ€ ๋…ธ๋“œ๋ฅผ ํŽผ์น˜๊ฑฐ๋‚˜ ์ ‘์„ ๋•Œ ์นด๋ฉ”๋ผ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ์ง€ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

MindMapWidget(
  data: myData,
  nodeExpandCameraBehavior: NodeExpandCameraBehavior.focusClickedNode,
)

Node Expand Camera Options / ๋…ธ๋“œ ํ™•์žฅ ์นด๋ฉ”๋ผ ์˜ต์…˜

Behavior / ๋™์ž‘ Description / ์„ค๋ช…
NodeExpandCameraBehavior.none No camera movement (default) / ์นด๋ฉ”๋ผ ์ด๋™ ์—†์Œ (๊ธฐ๋ณธ๊ฐ’)
NodeExpandCameraBehavior.focusClickedNode Focus on the clicked node / ํด๋ฆญํ•œ ๋…ธ๋“œ๋กœ ํฌ์ปค์Šค
NodeExpandCameraBehavior.fitExpandedChildren Fit newly expanded children to view / ์ƒˆ๋กœ ํŽผ์ณ์ง„ ์ž์‹ ๋…ธ๋“œ๋“ค์ด ๋ณด์ด๋„๋ก ์กฐ์ •
NodeExpandCameraBehavior.fitExpandedSubtree Fit entire expanded subtree to view / ํŽผ์ณ์ง„ ์ „์ฒด ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ๋ณด์ด๋„๋ก ์กฐ์ •

Practical Examples / ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

1. Focus on Clicked Node / ํด๋ฆญํ•œ ๋…ธ๋“œ์— ํฌ์ปค์Šค

MindMapWidget(
  data: myData,
  nodeExpandCameraBehavior: NodeExpandCameraBehavior.focusClickedNode,
  focusAnimation: Duration(milliseconds: 400),
)

2. Show All Expanded Children / ํŽผ์ณ์ง„ ๋ชจ๋“  ์ž์‹ ๋…ธ๋“œ ํ‘œ์‹œ

MindMapWidget(
  data: myData,
  nodeExpandCameraBehavior: NodeExpandCameraBehavior.fitExpandedChildren,
  focusAnimation: Duration(milliseconds: 500),
)

3. Show Entire Subtree / ์ „์ฒด ์„œ๋ธŒํŠธ๋ฆฌ ํ‘œ์‹œ

MindMapWidget(
  data: myData,
  nodeExpandCameraBehavior: NodeExpandCameraBehavior.fitExpandedSubtree,
  focusMargin: EdgeInsets.all(30),
)

Custom Styling / ์ปค์Šคํ…€ ์Šคํƒ€์ผ๋ง

final customStyle = MindMapStyle(
  layout: MindMapLayout.radial,
  nodeShape: NodeShape.circle,
  enableAutoSizing: true,
  connectionColor: Colors.blue,
  animationDuration: Duration(milliseconds: 600),
  defaultNodeColors: [Colors.blue, Colors.green, Colors.orange],
);

Event Handling / ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

MindMapWidget(
  data: myData,
  onNodeTap: (node) => print('Node tapped: ${node.title}'),
  onNodeLongPress: (node) => _showNodeOptions(node),
  onNodeExpandChanged: (node, isExpanded) => 
    print('${node.title} ${isExpanded ? 'expanded' : 'collapsed'}'),
);

Available Options / ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์˜ต์…˜

Layouts / ๋ ˆ์ด์•„์›ƒ

Layout / ๋ ˆ์ด์•„์›ƒ Description / ์„ค๋ช…
MindMapLayout.right Traditional right-expanding / ์˜ค๋ฅธ์ชฝ ํ™•์žฅ
MindMapLayout.left Left-expanding / ์™ผ์ชฝ ํ™•์žฅ
MindMapLayout.top Upward-expanding / ์œ„์ชฝ ํ™•์žฅ
MindMapLayout.bottom Downward-expanding / ์•„๋ž˜์ชฝ ํ™•์žฅ
MindMapLayout.radial Circular arrangement / ์›ํ˜• ๋ฐฐ์น˜
MindMapLayout.horizontal Left-right split / ์ขŒ์šฐ ๋ถ„ํ• 
MindMapLayout.vertical Top-bottom split / ์ƒํ•˜ ๋ถ„ํ• 

Node Shapes / ๋…ธ๋“œ ๋ชจ์–‘

Shape / ๋ชจ์–‘ Description / ์„ค๋ช…
NodeShape.roundedRectangle Rounded corners (default) / ๋‘ฅ๊ทผ ๋ชจ์„œ๋ฆฌ (๊ธฐ๋ณธ)
NodeShape.circle Perfect circle / ์™„์ „ํ•œ ์›
NodeShape.rectangle Sharp corners / ๋‚ ์นด๋กœ์šด ๋ชจ์„œ๋ฆฌ
NodeShape.diamond Diamond shape / ๋‹ค์ด์•„๋ชฌ๋“œ ๋ชจ์–‘
NodeShape.hexagon Six-sided polygon / ์œก๊ฐํ˜•
NodeShape.ellipse Oval shape / ํƒ€์›ํ˜•

Performance / ์„ฑ๋Šฅ

  • ์ตœ์ ํ™”๋œ ๋ Œ๋”๋ง / Optimized rendering with custom painters
  • ๋™์  ๊ฐ„๊ฒฉ ๊ณ„์‚ฐ / Smart spacing based on content
  • ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์  / Minimal widget tree overhead
  • ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ / Hardware-accelerated animations

License / ๋ผ์ด์„ ์Šค

This project is licensed under the MIT License - see the LICENSE file for details.

์ด ํ”„๋กœ์ ํŠธ๋Š” MIT ๋ผ์ด์„ ์Šค ํ•˜์— ์žˆ์Šต๋‹ˆ๋‹ค - ์ž์„ธํ•œ ๋‚ด์šฉ์€ LICENSE ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

Contributing / ๊ธฐ์—ฌ

We welcome contributions! Whether you're fixing bugs, adding features, or improving documentation, your help is appreciated.

๊ธฐ์—ฌ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ๋ฒ„๊ทธ ์ˆ˜์ •, ๊ธฐ๋Šฅ ์ถ”๊ฐ€, ๋ฌธ์„œ ๊ฐœ์„  ๋“ฑ ๋ชจ๋“  ๋„์›€์„ ๊ฐ์‚ฌํžˆ ๋ฐ›๊ฒ ์Šต๋‹ˆ๋‹ค.

Quick Contributing Guide / ๋น ๋ฅธ ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ

  1. ๐Ÿ› Found a bug? / ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ•˜์…จ๋‚˜์š”?

  2. ๐Ÿ’ก Have a feature idea? / ๊ธฐ๋Šฅ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์œผ์‹ ๊ฐ€์š”?

  3. โ“ Need help? / ๋„์›€์ด ํ•„์š”ํ•˜์‹ ๊ฐ€์š”?

  4. ๐Ÿ”ง Want to contribute code? / ์ฝ”๋“œ ๊ธฐ์—ฌ๋ฅผ ์›ํ•˜์‹œ๋‚˜์š”?

    • Read our detailed Contributing Guide / ์ƒ์„ธํ•œ ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”
    • Fork the repo, make changes, and submit a PR / ์ €์žฅ์†Œ๋ฅผ ํฌํฌํ•˜๊ณ  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋งŒ๋“  ํ›„ PR์„ ์ œ์ถœํ•˜์„ธ์š”

Development Setup / ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •

git clone https://github.com/YOUR_USERNAME/reactive_mind_map.git
cd reactive_mind_map
flutter pub get
flutter run

For detailed development guidelines, coding standards, and contribution process, please see our Contributing Guide.

์ž์„ธํ•œ ๊ฐœ๋ฐœ ๊ฐ€์ด๋“œ๋ผ์ธ, ์ฝ”๋”ฉ ํ‘œ์ค€, ๊ธฐ์—ฌ ๊ณผ์ •์€ ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

Issues / ์ด์Šˆ

If you encounter any issues or have feature requests, please file them in the GitHub Issues section.

์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋Šฅ ์š”์ฒญ์ด ์žˆ์œผ์‹œ๋ฉด GitHub Issues ์„น์…˜์— ๋“ฑ๋กํ•ด ์ฃผ์„ธ์š”.

๋ณ€๊ฒฝ ์ด๋ ฅ

์ตœ์‹  ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ CHANGELOG.md๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

About

A powerful mind map widget for Flutter, which supports multiple layouts, smart camera focus, interactive expand/collapse, custom node shapes, dynamic sizing, smooth animations, pan & zoom navigation, and rich styling options.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •