|  | // Copyright 2014 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | import 'dart:math' as math; | 
|  |  | 
|  | import 'package:flutter/material.dart'; | 
|  |  | 
|  | import '../rendering/src/sector_layout.dart'; | 
|  |  | 
|  | RenderBoxToRenderSectorAdapter initCircle() { | 
|  | return RenderBoxToRenderSectorAdapter(innerRadius: 25.0, child: RenderSectorRing()); | 
|  | } | 
|  |  | 
|  | class SectorApp extends StatefulWidget { | 
|  | const SectorApp({super.key}); | 
|  |  | 
|  | @override | 
|  | SectorAppState createState() => SectorAppState(); | 
|  | } | 
|  |  | 
|  | class SectorAppState extends State<SectorApp> { | 
|  | final RenderBoxToRenderSectorAdapter sectors = initCircle(); | 
|  | final math.Random rand = math.Random(1); | 
|  |  | 
|  | List<double> wantedSectorSizes = <double>[]; | 
|  | List<double> actualSectorSizes = <double>[]; | 
|  | double get currentTheta => | 
|  | wantedSectorSizes.fold<double>(0.0, (double total, double value) => total + value); | 
|  |  | 
|  | void addSector() { | 
|  | final double currentTheta = this.currentTheta; | 
|  | if (currentTheta < kTwoPi) { | 
|  | double deltaTheta; | 
|  | if (currentTheta >= kTwoPi - (math.pi * 0.2 + 0.05)) { | 
|  | deltaTheta = kTwoPi - currentTheta; | 
|  | } else { | 
|  | deltaTheta = math.pi * rand.nextDouble() / 5.0 + 0.05; | 
|  | } | 
|  | wantedSectorSizes.add(deltaTheta); | 
|  | updateEnabledState(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void removeSector() { | 
|  | if (wantedSectorSizes.isNotEmpty) { | 
|  | wantedSectorSizes.removeLast(); | 
|  | updateEnabledState(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void doUpdates() { | 
|  | int index = 0; | 
|  | while (index < actualSectorSizes.length && | 
|  | index < wantedSectorSizes.length && | 
|  | actualSectorSizes[index] == wantedSectorSizes[index]) { | 
|  | index += 1; | 
|  | } | 
|  | final RenderSectorRing ring = sectors.child! as RenderSectorRing; | 
|  | while (index < actualSectorSizes.length) { | 
|  | ring.remove(ring.lastChild!); | 
|  | actualSectorSizes.removeLast(); | 
|  | } | 
|  | while (index < wantedSectorSizes.length) { | 
|  | final Color color = Color(((0xFF << 24) + rand.nextInt(0xFFFFFF)) | 0x808080); | 
|  | ring.add(RenderSolidColor(color, desiredDeltaTheta: wantedSectorSizes[index])); | 
|  | actualSectorSizes.add(wantedSectorSizes[index]); | 
|  | index += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static RenderBoxToRenderSectorAdapter initSector(Color color) { | 
|  | final RenderSectorRing ring = RenderSectorRing(padding: 1.0); | 
|  | ring.add(RenderSolidColor(const Color(0xFF909090), desiredDeltaTheta: kTwoPi * 0.15)); | 
|  | ring.add(RenderSolidColor(const Color(0xFF909090), desiredDeltaTheta: kTwoPi * 0.15)); | 
|  | ring.add(RenderSolidColor(color, desiredDeltaTheta: kTwoPi * 0.2)); | 
|  | return RenderBoxToRenderSectorAdapter(innerRadius: 5.0, child: ring); | 
|  | } | 
|  |  | 
|  | RenderBoxToRenderSectorAdapter sectorAddIcon = initSector(const Color(0xFF00DD00)); | 
|  | RenderBoxToRenderSectorAdapter sectorRemoveIcon = initSector(const Color(0xFFDD0000)); | 
|  |  | 
|  | bool _enabledAdd = true; | 
|  | bool _enabledRemove = false; | 
|  | void updateEnabledState() { | 
|  | setState(() { | 
|  | _enabledAdd = currentTheta < kTwoPi; | 
|  | _enabledRemove = wantedSectorSizes.isNotEmpty; | 
|  | }); | 
|  | } | 
|  |  | 
|  | void recursivelyDisposeChildren(RenderObject parent) { | 
|  | parent.visitChildren((RenderObject child) { | 
|  | recursivelyDisposeChildren(child); | 
|  | child.dispose(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | Widget buildBody() { | 
|  | return Column( | 
|  | mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
|  | children: <Widget>[ | 
|  | Container( | 
|  | padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 25.0), | 
|  | child: Row( | 
|  | mainAxisAlignment: MainAxisAlignment.spaceAround, | 
|  | children: <Widget>[ | 
|  | ElevatedButton( | 
|  | onPressed: _enabledAdd ? addSector : null, | 
|  | child: IntrinsicWidth( | 
|  | child: Row( | 
|  | children: <Widget>[ | 
|  | Container( | 
|  | padding: const EdgeInsets.all(4.0), | 
|  | margin: const EdgeInsets.only(right: 10.0), | 
|  | child: WidgetToRenderBoxAdapter( | 
|  | renderBox: sectorAddIcon, | 
|  | onUnmount: () { | 
|  | recursivelyDisposeChildren(sectorAddIcon); | 
|  | }, | 
|  | ), | 
|  | ), | 
|  | const Text('ADD SECTOR'), | 
|  | ], | 
|  | ), | 
|  | ), | 
|  | ), | 
|  | ElevatedButton( | 
|  | onPressed: _enabledRemove ? removeSector : null, | 
|  | child: IntrinsicWidth( | 
|  | child: Row( | 
|  | children: <Widget>[ | 
|  | Container( | 
|  | padding: const EdgeInsets.all(4.0), | 
|  | margin: const EdgeInsets.only(right: 10.0), | 
|  | child: WidgetToRenderBoxAdapter( | 
|  | renderBox: sectorRemoveIcon, | 
|  | onUnmount: () { | 
|  | recursivelyDisposeChildren(sectorRemoveIcon); | 
|  | }, | 
|  | ), | 
|  | ), | 
|  | const Text('REMOVE SECTOR'), | 
|  | ], | 
|  | ), | 
|  | ), | 
|  | ), | 
|  | ], | 
|  | ), | 
|  | ), | 
|  | Expanded( | 
|  | child: Container( | 
|  | margin: const EdgeInsets.all(8.0), | 
|  | decoration: BoxDecoration(border: Border.all()), | 
|  | padding: const EdgeInsets.all(8.0), | 
|  | child: WidgetToRenderBoxAdapter( | 
|  | renderBox: sectors, | 
|  | onBuild: doUpdates, | 
|  | onUnmount: () { | 
|  | recursivelyDisposeChildren(sectors); | 
|  | }, | 
|  | ), | 
|  | ), | 
|  | ), | 
|  | ], | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | Widget build(BuildContext context) { | 
|  | return MaterialApp( | 
|  | title: 'Sector Layout', | 
|  | home: Scaffold( | 
|  | appBar: AppBar(title: const Text('Sector Layout in a Widget Tree')), | 
|  | body: buildBody(), | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | runApp(const SectorApp()); | 
|  | } |