两个小部件一个重新渲染,另一个不重新渲染

时间:2018-10-15 20:46:02

标签: dart flutter

关于重新渲染,我似乎“失去了泡沫”!我不确定我的应用的当前实现有什么问题。它是在许多SO成员的帮助下派生的。

应该做的:渲染9个绿色的圆圈,然后一个接一个地以递增或递减的顺序将每个圆圈呈现为黄色(“圆圈”小部件)。显示计数的当前值(“计数器”小部件)。在AppBar(“主页”页面小部件)中:识别+的抽头和递增计数,识别-的抽头和递减计数。在这两种情况下,都要在setState方法的主体中执行递增/递减操作。 “圆”和“计数器”小部件都有望重新渲染。在此实现中,所有圈子都参与了从绿色到黄色到绿色的颜色更改(有效计数被忽略了)。

它的作用:根据需要显示“圆”和“计数器”小部件的初始渲染。但是,尽管可以识别AppBar图标(+和-),但不会导致Circles小部件的重新渲染。计数器小部件确实重新渲染其计数显示。在“圆圈”小部件中有一个RaisedButton,当点击该按钮时,会引起“圆圈”小部件的重新渲染。但是该按钮在最终实现中是不需要的,仅用于测试。

让我感到困惑的是,“圈子”窗口小部件使用的模板与“计数器”窗口小部件使用的模板相同。但是它们的执行方式似乎有所不同。

整个应用程序的源代码如下。它是一个.dart文件(很长,很抱歉,但是过去遗漏了一些问题)。

有想法吗?

// ignore_for_file: camel_case_types
// ignore_for_file: constant_identifier_names
// ignore_for_file: non_constant_identifier_names

import 'package:flutter/material.dart';

import 'dart:async';
import 'dart:math';

const int     NUMBER_TILES = 9;
final int     CROSS_AXIS_COUNT = (sqrt(NUMBER_TILES)).toInt();
const double  CROSS_AXIS_SPACING = 4.0;
const int     INITIAL_COUNT = 9;        // for testing; should be 1
const double  MAIN_AXIS_SPACING = CROSS_AXIS_SPACING;
const int     MILLISECOND_MULTIPLIER = 500;

// ************************************************************** main

void main() {
  final AppState app_state = new AppState(counter: INITIAL_COUNT);
  runApp(new Home_Page(app_state: app_state));
} // main

// **************************************************** class AppState

class AppState {
  int       counter = 0;
  List<int> flash_indices = [];
  bool      forward = false;

  AppState({this.counter});             // AppState

  String toString() {                   // toString
    return ( 'AppState{' +
             'counter: $counter, ' +
             'flash_indices: $flash_indices}');
  } // toString
                                        // following is a mock
  void randomize_flash_indices ( ) {    // randomize_flash_indices

    forward = !forward;
    if ( forward){
      flash_indices = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, -1];
    }
    else {
      flash_indices = [ 8, 7, 6, 5, 4, 3, 2, 1, 0, -1];
    }
    flash_indices.add (-1);             // restore to normal colors
  } // randomize_flash_indices

} // class AppState

// *************************************************** class Home_Page

class Home_Page
      extends StatefulWidget {
  final AppState app_state;

  Home_Page({
    @required this.app_state,
    Key key,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return Home_Page_State();
  }

} // class Home_Page

// ********************************************* class Home_Page_State

class Home_Page_State extends State<Home_Page>{

  Home_Page_State();

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Periodic',
      theme: new ThemeData(primarySwatch: Colors.indigo),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Periodic'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.add),    // increment counter
              onPressed: () {
                if (widget.app_state.counter < NUMBER_TILES){
                  setState(() {
                    widget.app_state.counter++;
                  });
                }
              }
            ),
            IconButton(
              icon: Icon(Icons.remove), // decrement counter
              onPressed: () {
                if (widget.app_state.counter > 1){
                  setState(() {
                    widget.app_state.counter--;
                  });
                }
              }
            ),
          ]
        ),
        body: Column(
          children: [
            Circles (
              app_state: widget.app_state,
            ),
            Counter (
              app_state: widget.app_state,
            )
          ],
        ),
      ),

    );
  } // Home_Page_State build

} // class Home_Page_State

// ***************************************************** class Circles

class Circles extends StatefulWidget {
  final AppState app_state;

  Circles({
    @required this.app_state,
    Key key,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return Circles_State();
  }
} // class Circles

// *********************************************** class Circles_State

class Circles_State extends State<Circles>{

  Circles_State();

  int                     flash_tile = -1;
  List<GridTile>          grid_tiles = <GridTile>[];
  StreamController<int>   tick_controller;
  StreamSubscription<int> tick_listener;

  Stream<int> start_ticking() {         // start_ticking
    tick_controller = new StreamController();

    for ( int tick = 0; (tick < widget.app_state.counter); tick++ ) {
      Future.delayed(Duration(milliseconds:
                     MILLISECOND_MULTIPLIER * tick),() {

print('start_ticking() tick: $tick');

        tick_controller.add(tick);
      });
    }
    return tick_controller.stream;
  } // start_ticking

  @override
  void initState() {                    // initState
    super.initState();
    widget.app_state.randomize_flash_indices();
    tick_listener = start_ticking().listen(on_tick);
  } // initState

  @override
  void dispose() {                      // dispose
    if (tick_listener != null) {
      tick_listener.cancel();
      tick_listener = null;
    }
    super.dispose();
  } // dispose

  on_tick(int tick) async { // on_tick

print('listen_for_tick() tick: $tick');

    this.setState(() => this.flash_tile =
                        widget.app_state.
                               flash_indices[tick]);
  } // on_tick

  GridTile new_circle_tile(             // new_circle_tile
                    Color tile_color,
                    int   index) {
    GridTile tile = GridTile(
        child: GestureDetector(
          child: Container(
            decoration: BoxDecoration(
              color: tile_color,
              shape: BoxShape.circle,
            ),
          ),
        )
      );
    return (tile);
  } // new_circle_tile

  List<GridTile> create_circle_tiles() {// create_circle_tiles
    grid_tiles = new List<GridTile>();

    for (int i = 0; (i < NUMBER_TILES); i++) {
      Color tile_color =
              ( this.flash_tile == i) ?
                        Colors.yellow :
                        Colors.green;

      grid_tiles.add(new_circle_tile(tile_color, i));
    }
    return (grid_tiles);
  } // create_circle_tiles

  @override // Circles_State
  Widget build(BuildContext context) {

print('Circles_State Build ' +
      widget.app_state.toString() +
      ' flash_tile: $flash_tile');

    return Column(
      children: [
        GridView.count(
          shrinkWrap: true,
          crossAxisCount: CROSS_AXIS_COUNT,
          childAspectRatio: 1.0,
          padding: const EdgeInsets.all(4.0),
          mainAxisSpacing: MAIN_AXIS_SPACING,
          crossAxisSpacing: CROSS_AXIS_SPACING,
          children: create_circle_tiles(),
        ),
        RaisedButton(
          child: Text("restart"),
          onPressed: () {
            widget.app_state.randomize_flash_indices();
            tick_listener = start_ticking().listen(on_tick);
          }
        ),
      ] // children
    );
  } // Circles_State build

} // class Circles_State

// ***************************************************** class Counter

class Counter extends StatefulWidget {
  final AppState app_state;

  Counter({
    @required this.app_state,
    Key key,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return Counter_State();
  }
} // class Counter

// *********************************************** class Counter_State

class Counter_State extends State<Counter> {

  Counter_State();

  @override // Counter_State
  Widget build(BuildContext context) {
    int counter_value = widget.app_state.counter;
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Expanded(
          child: SizedBox(
            width: 24.0,
            child: Center(
              child: Text(
                'Counter $counter_value',
                style: TextStyle(
                  color: Colors.blue,
                  fontWeight: FontWeight.bold,
                  fontSize: 24.0,
                ),
              ),
            ),
          ),
        ),
      ],
    );
  } // Counter_State build

} // class Counter_State

1 个答案:

答案 0 :(得分:0)

我对找到的解决方案感到非常失望-将所有小部件组合到一个类中。原始架构的骨架看起来像:

void main(){
  :
} // main()

class AppState {
  :
} // class AppState

class HomePage extends StatefulWidget {
  :
} // class HomePage

class HomePageState extends State<HomePage>{ 
  :
} // class HomePageState

class Circles extends StatefulWidget {
  :
} // class Circles

class CirclesState extends State<Circles>{}
  :
} // class CirclesState

class Counter extends StatefulWidget {
  :
} // class Counter

class CounterState extends State<Counter>{}
  :
} // class CounterState

每个类都可以放入其自己的 .dart 文件中。但是,为了获得所需的结果,我必须消除除 HomePage HomePageState 之外的所有类。所有其他类(变量,方法和函数)的内容都必须放入 HomePageState 中,以便在状态更改时,小部件可以正确更新。修改后的实现的框架如下:

void main(){
  :
} // main()

class HomePage extends StatefulWidget {
  :
} // class HomePage

class HomePageState extends State<HomePage>{ 
  :
  void randomize_flash_indices ( ) {...

  Stream<int> start_ticking() { ...

  @override
  void initState() { ...

  @override
  void dispose() { ...

  on_tick(int tick) async { ...

  Circles(){ ...

  Counter(){ ...

  @override
  Widget build(BuildContext context) {...

  :
} // class HomePageState

此实现是整体的,并且面对良好的编程(和设计)(和体系结构)原则。这种软件无法在生产环境中维护。

似乎Dart语言的设计师错过了关于setState()的要点,或者我完全错过了正确的编码实践。因为我是一位经验丰富的程序员(超过42年),所以我常常会怀疑后者(当然,因为,我是一位经验丰富的程序员,所以我意识到我可能缺少关于setState的重要知识)。

我对扑扑/飞镖感到失望。我曾希望Xamarin有所帮助-我猜我不会从扑打/飞镖中得到它。