// 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( 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()); }