Flutter मोबाइल विकास · पाठ

इंटरैक्टिव कस्टम चार्ट विजेट बनाना

स्पर्श-प्रतिक्रियाशील डेटा चार्ट रेंडर करने के लिए CustomPainter को जेस्चर पहचान के साथ मिलाइए।

पाठ 2, कुल 4 में से13 चरण

इंटरैक्टिव कस्टम चार्ट विजेट बनाना, CoddyKit पर Flutter मोबाइल विकास का एक निःशुल्क पाठ है। यह 4 में से 2वाँ पाठ है। इस अध्ययन पथ के 3 तक कोई भी पाठ पूरा पढ़ना निःशुल्क है — इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ व्यावहारिक अभ्यास भी उपलब्ध कराता है। यह Flutter मोबाइल विकास सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Flutter मोबाइल विकास पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

इंटरैक्टिव कस्टम चार्ट क्यों?

Flutter में पहले से कोई अंतर्निर्मित चार्ट विजेट नहीं आता। जब आपको ऐसा स्पर्श-प्रतिक्रियाशील बार या लाइन चार्ट चाहिए जो आपके डिज़ाइन से बिल्कुल मेल खाए, तो आप दो मूल घटकों को मिलाते हैं:

  • CustomPainter — चार्ट को Canvas पर बनाता है।
  • GestureDetector — टैप और ड्रैग को पकड़ता है, ताकि उपयोगकर्ता डेटा-बिंदुओं को हाइलाइट या जाँच सके।

इस पाठ में हम ऐसा बार चार्ट बनाएँगे जो उपयोगकर्ता द्वारा छुए गए बार को हाइलाइट करता है और उसका मान बताता है। मुख्य विचार यह है: पेंटर स्थिति का शुद्ध फलन है और जेस्चर उस स्थिति को बदलते हैं।

डेटा मॉडल

एक सादे अपरिवर्तनीय मॉडल से शुरुआत करें। इसे फ़्रेमवर्क से स्वतंत्र रखने पर परीक्षण करना और इसके व्यवहार को समझना आसान होता है। हर बार में एक लेबल और एक मान होता है।

यह स्निपेट बिना किसी Flutter इंपोर्ट के शुद्ध Dart है, इसलिए यह स्वतंत्र रूप से चलता है।

class Bar {
  final String label;
  final double value;
  const Bar(this.label, this.value);
}

void main() {
  final data = <Bar>[
    const Bar('Mon', 12),
    const Bar('Tue', 30),
    const Bar('Wed', 8),
    const Bar('Thu', 22),
  ];
  final maxValue = data.map((b) => b.value).reduce((a, b) => a > b ? a : b);
  print('Bars: ${data.length}, max value: $maxValue');
  for (final b in data) {
    final ratio = (b.value / maxValue * 100).toStringAsFixed(0);
    print('${b.label}: $ratio% of tallest');
  }
}

CustomPainter का ढाँचा

एक CustomPainter दो विधियाँ लागू करता है:

  • paint(Canvas canvas, Size size) — जहाँ आप चित्र बनाते हैं।
  • shouldRepaint(old) — इनपुट बदलने पर true लौटाएँ, ताकि Flutter दोबारा चित्र बनाए।

अपने डेटा और किसी भी इंटरैक्शन स्थिति, जैसे हाइलाइट किए गए इंडेक्स, को पेंटर के कंस्ट्रक्टर में final फ़ील्ड के रूप में पास करें।

class BarChartPainter extends CustomPainter {
  final List<Bar> bars;
  final int? highlightedIndex;

  BarChartPainter({required this.bars, this.highlightedIndex});

  @override
  void paint(Canvas canvas, Size size) {
    // drawing logic goes here
  }

  @override
  bool shouldRepaint(covariant BarChartPainter old) {
    return old.bars != bars || old.highlightedIndex != highlightedIndex;
  }
}

बार ज्यामिति की गणना

चित्र बनाने से पहले, हर डेटा मान को पिक्सेल निर्देशांकों में मैप करें। Canvas के size के आधार पर चौड़ाई को समान स्लॉट में बाँटें और ऊँचाइयों को अधिकतम मान के अनुसार स्केल करें।

हर बार का Rect सहेजकर रखने से आप बाद में हिट-टेस्टिंग के लिए बिल्कुल वही ज्यामिति फिर से उपयोग कर सकते हैं — चित्रण और जेस्चर का आपस में मेल खाना आवश्यक है।

List<Rect> computeBarRects(List<Bar> bars, Size size) {
  final maxValue = bars.map((b) => b.value).reduce((a, b) => a > b ? a : b);
  final slotWidth = size.width / bars.length;
  const barFraction = 0.6; // bars take 60% of their slot
  final rects = <Rect>[];
  for (var i = 0; i < bars.length; i++) {
    final barWidth = slotWidth * barFraction;
    final left = i * slotWidth + (slotWidth - barWidth) / 2;
    final height = (bars[i].value / maxValue) * size.height;
    final top = size.height - height;
    rects.add(Rect.fromLTWH(left, top, barWidth, height));
  }
  return rects;
}

बार बनाना

paint के अंदर एक Paint ऑब्जेक्ट बनाएँ और हर बार के लिए canvas.drawRect कॉल करें। छुए गए बार को अलग रंग देने के लिए highlightedIndex का उपयोग करें।

सुझाव: जब गुण न बदलते हों, तो पेंट को लूप के बाहर एक बार बनाएँ और हर बार केवल color बदलें।

@override
void paint(Canvas canvas, Size size) {
  final rects = computeBarRects(bars, size);
  final paint = Paint()..style = PaintingStyle.fill;
  for (var i = 0; i < rects.length; i++) {
    paint.color = (i == highlightedIndex)
        ? const Color(0xFFFF7043) // highlighted
        : const Color(0xFF42A5F5); // default
    final rounded = RRect.fromRectAndRadius(
      rects[i],
      const Radius.circular(4),
    );
    canvas.drawRRect(rounded, paint);
  }
}

TextPainter से लेबल बनाना

Canvas सीधे पाठ नहीं बना सकता — इसके लिए आपको TextPainter का उपयोग करना होगा। पहले पाठ का लेआउट करें, फिर उसे मनचाही स्थिति पर paint करें। हर लेबल को उसके बार के नीचे बीच में रखें।

void drawLabel(Canvas canvas, String text, Offset center) {
  final tp = TextPainter(
    text: TextSpan(
      text: text,
      style: const TextStyle(color: Color(0xFF333333), fontSize: 12),
    ),
    textDirection: TextDirection.ltr,
  )..layout();
  final offset = Offset(center.dx - tp.width / 2, center.dy);
  tp.paint(canvas, offset);
}

Painter को विजेट में रखना

अपने पेंटर को CustomPaint विजेट में लपेटें। उसे एक size दें, या उसे फैलने दें, ताकि उसके पास चित्र बनाने के लिए सीमाएँ हों।

चूँकि हाइलाइट इंटरैक्शन स्थिति है, होस्ट एक StatefulWidget होता है, जो _highlightedIndex को रखता है और उसके बदलने पर फिर से बनता है।

class BarChart extends StatefulWidget {
  final List<Bar> bars;
  const BarChart({super.key, required this.bars});
  @override
  State<BarChart> createState() => _BarChartState();
}

class _BarChartState extends State<BarChart> {
  int? _highlightedIndex;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: const Size(double.infinity, 200),
      painter: BarChartPainter(
        bars: widget.bars,
        highlightedIndex: _highlightedIndex,
      ),
    );
  }
}

टैप का हिट-टेस्ट करना

यह जानने के लिए कि किस बार को छुआ गया है, उन्हीं rects को फिर से निकालें और जाँचें कि टच बिंदु उनमें से किसके भीतर आता है। एक शुद्ध सहायक फ़ंक्शन इस तर्क को test करने योग्य रखता है और painter द्वारा बनाए गए तर्क के समान बनाए रखता है।

यह स्निपेट स्वतंत्र Dart कोड है — इसमें एक आयत और वही hit-test गणना दिखाई गई है, जिसका उपयोग widget करता है।

class Box {
  final double left, top, width, height;
  const Box(this.left, this.top, this.width, this.height);
  bool contains(double x, double y) =>
      x >= left && x <= left + width && y >= top && y <= top + height;
}

int? hitTest(List<Box> boxes, double x, double y) {
  for (var i = 0; i < boxes.length; i++) {
    if (boxes[i].contains(x, y)) return i;
  }
  return null;
}

void main() {
  final boxes = [
    const Box(0, 100, 40, 100),
    const Box(60, 50, 40, 150),
    const Box(120, 120, 40, 80),
  ];
  print(hitTest(boxes, 70, 90));   // 1
  print(hitTest(boxes, 200, 10));  // null
}

GestureDetector को जोड़ना

CustomPaint को GestureDetector में लपेटें। स्थानीय टच स्थिति पढ़ने के लिए onTapDown का उपयोग करें, hit-test चलाएँ और highlight अपडेट करने के लिए setState कॉल करें।

वैश्विक स्थिति के बजाय details.localPosition का उपयोग करें, जो पहले से ही widget के सापेक्ष होती है।

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTapDown: (details) {
      final size = context.size!;
      final rects = computeBarRects(widget.bars, size);
      final p = details.localPosition;
      int? hit;
      for (var i = 0; i < rects.length; i++) {
        if (rects[i].contains(p)) { hit = i; break; }
      }
      setState(() => _highlightedIndex = hit);
    },
    child: CustomPaint(
      size: const Size(double.infinity, 200),
      painter: BarChartPainter(
        bars: widget.bars,
        highlightedIndex: _highlightedIndex,
      ),
    ),
  );
}

खींचने पर प्रतिक्रिया देना

स्क्रबिंग अनुभव के लिए, जिसमें उँगली को bars पर खींचकर हर बार की जानकारी देखी जाती है, onTapDown के साथ onPanUpdate को भी संभालें। उँगली के हिलने पर वही hit-test लगातार चलता रहता है।

highlight अपडेट को एक सहायक फ़ंक्शन में रखें, ताकि tap और drag एक ही code path साझा करें।

void _updateHighlight(Offset local) {
  final size = context.size!;
  final rects = computeBarRects(widget.bars, size);
  int? hit;
  for (var i = 0; i < rects.length; i++) {
    if (rects[i].contains(local)) { hit = i; break; }
  }
  if (hit != _highlightedIndex) {
    setState(() => _highlightedIndex = hit);
  }
}

// in build():
// onTapDown: (d) => _updateHighlight(d.localPosition),
// onPanUpdate: (d) => _updateHighlight(d.localPosition),

चयनित मान दिखाना

अब प्रतिक्रिया दिखाएँ। chart के नीचे ऐसा Text render करें जो highlighted bar के अनुसार बदले, या चयनित bar के ऊपर canvas पर tooltip बनाएँ।

  • painter को UI text के मामले में stateless रखें — उसे केवल highlighted index दें।
  • paint में भारी काम करने से बचें; geometry पहले से निकालकर drawing और hit-testing, दोनों में उसका पुनः उपयोग करें।
  • shouldRepaint को सटीक बनाएँ, ताकि हर frame पर repaint न हो।
Widget _selectionLabel() {
  if (_highlightedIndex == null) {
    return const Text('Tap a bar to inspect');
  }
  final bar = widget.bars[_highlightedIndex!];
  return Text('${bar.label}: ${bar.value.toStringAsFixed(1)}',
      style: const TextStyle(fontWeight: FontWeight.bold));
}

त्वरित जाँच

जाँचें कि drawing और gesture handling किस तरह एकसमान बने रहते हैं।

पुनरावलोकन

आपने ज़िम्मेदारियों को अलग करके टच-प्रतिक्रियाशील chart बनाया:

  • Data model — सरल, अपरिवर्तनीय और test करने योग्य।
  • Geometry helper — एक फ़ंक्शन मानों को Rects में बदलता है और drawing तथा hit-testing, दोनों में साझा किया जाता है।
  • CustomPainter — data और highlighted index के आधार पर bars और labels बनाता है तथा इसमें सटीक shouldRepaint होता है।
  • StatefulWidget — interaction state रखता है और setState कॉल करता है।
  • GestureDetector — onTapDown और onPanUpdate, localPosition पर साझा hit-test चलाते हैं।

स्वर्णिम नियम: painter को state का शुद्ध फ़ंक्शन बनाए रखें और state में बदलाव करने का काम केवल gestures को करने दें।

शुरुआत निःशुल्क

एआई शिक्षक के साथ Dart सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
22
पाठ
88

अक्सर पूछे जाने वाले प्रश्न

क्या “इंटरैक्टिव कस्टम चार्ट विजेट बनाना” पाठ निःशुल्क है?

हाँ — Flutter मोबाइल विकास अध्ययन पथ के 3 तक कोई भी पाठ, जिसमें “इंटरैक्टिव कस्टम चार्ट विजेट बनाना” भी शामिल है, यहाँ वेब पर पूरा पढ़ना निःशुल्क है। इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ इंटरैक्टिव अभ्यास भी उपलब्ध कराता है। Flutter मोबाइल विकास पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“इंटरैक्टिव कस्टम चार्ट विजेट बनाना” में मैं क्या सीखूँगा?

स्पर्श-प्रतिक्रियाशील डेटा चार्ट रेंडर करने के लिए CustomPainter को जेस्चर पहचान के साथ मिलाइए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Flutter मोबाइल विकास का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या Flutter मोबाइल विकास शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Flutter मोबाइल विकास शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 2वाँ पाठ है।

“इंटरैक्टिव कस्टम चार्ट विजेट बनाना” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस Flutter मोबाइल विकास पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर Flutter मोबाइल विकास पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. Canvas API: पथ, पेंट और शेडर
  2. इंटरैक्टिव कस्टम चार्ट विजेट बनाना
  3. क्लिपिंग, ब्लेंड मोड और लेयर कंपोज़िटिंग
  4. हिट टेस्टिंग और रीपेंट अनुकूलन
← Flutter मोबाइल विकास पर वापस जाएँ