// tools/chi_square.dart import 'package:flutter/material.dart'; import 'package:rational/rational.dart'; import 'package:fl_chart/fl_chart.dart'; import '../utils/math_utils.dart'; class ChiSquarePage extends StatefulWidget { @override _ChiSquarePageState createState() => _ChiSquarePageState(); } class _ChiSquarePageState extends State { final TextEditingController aController = TextEditingController(); final TextEditingController bController = TextEditingController(); final TextEditingController cController = TextEditingController(); final TextEditingController dController = TextEditingController(); String result = ''; List barGroups = []; bool showChart = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('卡方检验')), body: Padding( padding: EdgeInsets.all(16), child: Column( children: [ Text('请输入2×2列联表数据:'), SizedBox(height: 20), Row( children: [ Expanded(child: _buildInputField('a', aController)), SizedBox(width: 10), Expanded(child: _buildInputField('b', bController)), ], ), Row( children: [ Expanded(child: _buildInputField('c', cController)), SizedBox(width: 10), Expanded(child: _buildInputField('d', dController)), ], ), SizedBox(height: 20), ElevatedButton( onPressed: _calculate, child: Text('计算卡方值'), ), SizedBox(height: 20), if (result.isNotEmpty) Card( child: Padding( padding: EdgeInsets.all(16), child: Text( result, style: TextStyle(fontSize: 16), ), ), ), SizedBox(height: 20), if (showChart && barGroups.isNotEmpty) Expanded( child: Card( elevation: 4, child: Padding( padding: EdgeInsets.all(16), child: Column( children: [ Text( '等高堆积条形图', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), SizedBox(height: 10), Expanded( child: BarChart( BarChartData( alignment: BarChartAlignment.spaceAround, maxY: 1.0, barTouchData: BarTouchData( enabled: false, ), titlesData: FlTitlesData( show: true, bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { switch (value.toInt()) { case 0: return Text('组1'); case 1: return Text('组2'); default: return Text(''); } }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { return Text('${(value * 100).toInt()}%'); }, reservedSize: 40, ), ), topTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), gridData: FlGridData( show: true, drawVerticalLine: false, getDrawingHorizontalLine: (value) { return FlLine( color: Colors.grey.withOpacity(0.3), strokeWidth: 1, ); }, ), borderData: FlBorderData( show: false, ), barGroups: barGroups, ), ), ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildLegend(Colors.blue, '类别A'), SizedBox(width: 20), _buildLegend(Colors.red, '类别B'), ], ), ], ), ), ), ), ], ), ), ); } Widget _buildInputField(String label, TextEditingController controller) { return TextField( controller: controller, keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), decoration: InputDecoration( labelText: label, border: OutlineInputBorder(), ), ); } Widget _buildLegend(Color color, String text) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 16, height: 16, color: color, ), SizedBox(width: 8), Text(text), ], ); } void _calculate() { try { final a = int.parse(aController.text); final b = int.parse(bController.text); final c = int.parse(cController.text); final d = int.parse(dController.text); final n = a + b + c + d; final numerator = Rational.fromInt(n) * Rational.fromInt((a * d - b * c) * (a * d - b * c)); final denominator = Rational.fromInt((a + b) * (c + d) * (a + c) * (b + d)); final K2 = numerator / denominator; // 计算比例用于图表 final totalGroup1 = a + b; final totalGroup2 = c + d; final proportionA1 = totalGroup1 > 0 ? a / totalGroup1 : 0.0; final proportionB1 = totalGroup1 > 0 ? b / totalGroup1 : 0.0; final proportionA2 = totalGroup2 > 0 ? c / totalGroup2 : 0.0; final proportionB2 = totalGroup2 > 0 ? d / totalGroup2 : 0.0; // 创建条形图数据 - 修复堆积问题 barGroups = [ BarChartGroupData( x: 0, groupVertically: true, // 启用垂直堆积 barsSpace: 4, // 条形之间的间距 barRods: [ BarChartRodData( fromY: 0, toY: proportionA1, color: Colors.blue, width: 30, ), BarChartRodData( fromY: proportionA1, // 从第一个条形的顶部开始 toY: proportionA1 + proportionB1, color: Colors.red, width: 30, ), ], ), BarChartGroupData( x: 1, groupVertically: true, // 启用垂直堆积 barsSpace: 4, // 条形之间的间距 barRods: [ BarChartRodData( fromY: 0, toY: proportionA2, color: Colors.blue, width: 30, ), BarChartRodData( fromY: proportionA2, // 从第一个条形的顶部开始 toY: proportionA2 + proportionB2, color: Colors.red, width: 30, ), ], ), ]; setState(() { result = 'K² = ${MathUtils.rationalToString(K2)}\n' '近似值: ${K2.toDouble().toStringAsFixed(4)}'; showChart = true; }); } catch (e) { setState(() { result = '错误: $e'; showChart = false; }); } } @override void dispose() { aController.dispose(); bController.dispose(); cController.dispose(); dController.dispose(); super.dispose(); } }