Flutter仿學習強國填空題

下面是學習強國的效果
在這裏插入圖片描述

然後看下我實現的效果
在這裏插入圖片描述

重點有兩個部分:

  1. 使用RichText,然後根據答案的長度,動態設置需要填空的個數。RichText中有WidgetSpan,使用這個就能方便地在RichText中添加自定義的控件,Android中應該不能這麼簡單地實現。
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

  2. 設置一個佔位的TextField,它的寬度爲0,也就是相當於輸入框是隱藏的狀態,用於獲取用戶的輸入
    在這裏插入圖片描述

下面是完整代碼,這個代碼是簡化後的,因爲實際項目中邏輯會複雜很多。實際項目中因爲有填空題,單選題和多選題,挑戰答題,需要分組件去開發,可以使用provider來實現各個組件間的通信。在挑戰答題中還有一些動畫效果。如果想完全仿學習強國的業務邏輯,還是有些複雜的。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

//import 'learn_color_m.dart';

///填空題
//GlobalKey<_FillQuestionState> fillQuestionKey = GlobalKey();

class FillQuestion extends StatefulWidget {
  final fillQuesCallBack;

  FillQuestion({Key key, this.fillQuesCallBack}) : super(key: key);

  @override
  _FillQuestionState createState() => _FillQuestionState();
}

class _FillQuestionState extends State<FillQuestion> {
  TextEditingController controller = new TextEditingController();
  FocusNode textFieldFocusNode = new FocusNode();
  double boxSize;

  int answerCount; //答案的字數
  bool isFocus = false; //用戶是否點擊了填空的方框:因爲進入的時候第一個方框是不顯示邊框的,當用戶點擊了方框第一個方框就顯示邊框
  String fillAnswer; //題目的答案
  String answerValue = ""; //用戶輸入的答案


//  String stem = "";
  String stem1 =
      "要大力發展文學藝術、新聞出版、廣播影視等事業,弘揚民族優秀文化,吸收外國文化有益成果,加強"
  ;
  String stem2 = ",加強文化市場管理,營造良好的文化環境,不斷提高人民羣衆的文化生活質量,使人民羣衆充分享受自己創造的物質文化成果。";
  String fillResult = "文化基礎設施建設";
  String questionId = "";
  @override
  void initState() {
    super.initState();
    answerCount = fillResult.length;
    boxSize = 25;
  }

  @override
  Widget build(BuildContext context) {
    return _buildColumn();
  }

  Widget _buildColumn() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      children: <Widget>[
        RichText(
          strutStyle: StrutStyle(
            forceStrutHeight: false,
            height: 3,
          ),
          text: TextSpan(
            text: stem1,
            style: TextStyle(color: Colors.black, fontSize:15),
            children: <InlineSpan>[
              WidgetSpan(child: _buildPlaceholder()),
              TextSpan(
                children: _buildSpans(fillResult.length, answerValue, isFocus),
              ),
              TextSpan(text: stem2, style: TextStyle(color: Colors.black, fontSize: 15)),
            ],
          ),
        ),
      ],
    );
  }

  List<InlineSpan> _buildSpans(int count, String answerStr, bool isFocus) {
    List<InlineSpan> children = new List();
    TextSpan textSpan = new TextSpan(text: "  ");
    children.add(textSpan);
    int answerLength = answerStr.length;
    for (int i = 0; i < count; i++) {
      if (i < answerLength) {
        bool isShowBorder = false;
        if (i == count - 1) {
          ///如果所有空都填了,最後一個方框顯示邊框
          isShowBorder = true;
        }
        WidgetSpan widgetSpan = new WidgetSpan(child: _buildSpanChild(answerStr[i], isShowBorder));
        children.add(widgetSpan);
      } else {
        bool isShowBorder = false;
        if (i == answerLength && isFocus) {
          isShowBorder = true;
        }
        WidgetSpan widgetSpan = new WidgetSpan(child: _buildSpanChild(" ", isShowBorder));
        children.add(widgetSpan);
      }
      TextSpan textSpan = new TextSpan(text: "  ");
      children.add(textSpan);
    }
    return children;
  }

  Widget _buildSpanChild(String dataText, bool isShowBorder) {
    return GestureDetector(
      onTap: () {
        ///用戶點擊方框
        ///如果已經回答的情況,此時不應再彈出鍵盤(這裏只關心回答錯誤的情況,因爲回答正確直接跳入下一題)
//        if (isConfirm) {
//          return;
//        }
        if (!isFocus) {
          ///如果是首次點擊方框,就把第一個方框的邊框顯示出來
          setState(() {
            isFocus = true;
            answerValue = "";
          });
        }
        ///獲取鍵盤的焦點,並且主動調起鍵盤
        FocusScope.of(context).requestFocus(textFieldFocusNode); // 獲取焦點
        SystemChannels.textInput.invokeMethod<void>('TextInput.show'); //主動調起鍵盤,否則按返回鍵隱藏鍵盤後不能再次彈出
      },
      child: Container(
        alignment: Alignment.center,
        width: boxSize,
        height: boxSize,
        child: Text(
          dataText,
          style: getTextStyle(),
        ),
        decoration: getBoxDecoration(isShowBorder),
      ),
    );
  }

  getTextStyle() {
    Color color;

    ///確定了
//    if (isConfirm) {
//      ///正確了,顯示綠色;錯誤了,顯示紅色
//      color = isCorrect ? Color(0xff3EBE77) : Color(0xffF6444D);
//    }

    ///還未確定
//    else {
      color = Color(0xFF4b90c5);
//    }
    return TextStyle(color: color, fontSize: 15);
  }

  getBoxDecoration(bool isShowBorder) {
    Border border;

    ///確定了
//    if (isConfirm) {
//      ///正確了,顯示綠色;錯誤了,顯示紅色
//      border =
//          new Border.all(color: isCorrect ? Color(0xff3EBE77) : Color(0xffEEA6A5), width: ScreenUtil().setWidth(1));
//    }

    ///還未確定
//    else {
      border =
          isShowBorder ? new Border.all(color: Color(0xFFdbefff), width: 1) : null;
//    }
    return BoxDecoration(
      border: border,
      color: Color(0xFFF2F3F5),
    );
  }

  ///這個輸入框不顯示,只是用於獲取用戶的輸入
  Widget _buildPlaceholder() {
    return Container(
      width: 0,
      child: TextField(
          decoration: InputDecoration(
            counterText: '',
            fillColor: Colors.transparent,
          ),
          showCursor: false,
          style: TextStyle(color: Colors.transparent),
          maxLength: answerCount,
          focusNode: textFieldFocusNode,
          controller: controller,
          onChanged: (value) {
            setState(() {
              answerValue = value; //更新用戶輸入的答案,實時將答案顯示在方框中
            });
          }),
    );
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章