| // 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:material_ui/material_ui.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() { | |
| var index = 0; | |
| while (index < actualSectorSizes.length && | |
| index < wantedSectorSizes.length && | |
| actualSectorSizes[index] == wantedSectorSizes[index]) { | |
| index += 1; | |
| } | |
| final ring = sectors.child! as RenderSectorRing; | |
| while (index < actualSectorSizes.length) { | |
| ring.remove(ring.lastChild!); | |
| actualSectorSizes.removeLast(); | |
| } | |
| while (index < wantedSectorSizes.length) { | |
| final 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 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()); | |
| } |