| // 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({Key? key}) : super(key: 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( |
| theme: ThemeData.light(), |
| title: 'Sector Layout', |
| home: Scaffold( |
| appBar: AppBar( |
| title: const Text('Sector Layout in a Widget Tree'), |
| ), |
| body: buildBody(), |
| ), |
| ); |
| } |
| } |
| |
| void main() { |
| runApp(const SectorApp()); |
| } |